diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index a2555c99f..dbcf28963 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -1104,7 +1104,21 @@ DefineEngineFunction(ColorRGBToHSB, const char*, (ColorI color), , "@endtsexample\n" "@ingroup Strings") { - ColorI::Hsb hsb(color.getHSB()); + Hsb hsb(color.getHSB()); + String s(String::ToString(hsb.hue) + " " + String::ToString(hsb.sat) + " " + String::ToString(hsb.brightness)); + return Con::getReturnBuffer(s); +} + +DefineEngineFunction(ColorLinearRGBToHSB, const char*, (LinearColorF color), , + "Convert from a integer RGB (red, green, blue) color to HSB (hue, saturation, brightness). HSB is also know as HSL or HSV as well, with the last letter standing for lightness or value.\n" + "@param color Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha. It excepts an alpha, but keep in mind this will not be converted.\n" + "@return HSB color value, alpha isn't handled/converted so it is only the RGB value\n\n" + "@tsexample\n" + "ColorRBGToHSB( \"0 0 255 128\" ) // Returns \"240 100 100\".\n" + "@endtsexample\n" + "@ingroup Strings") +{ + Hsb hsb(color.getHSB()); String s(String::ToString(hsb.hue) + " " + String::ToString(hsb.sat) + " " + String::ToString(hsb.brightness)); return Con::getReturnBuffer(s); } @@ -1133,7 +1147,7 @@ DefineEngineFunction(ColorHSBToRGB, ColorI, (Point3I hsb), , "@ingroup Strings") { ColorI color; - color.set(ColorI::Hsb(hsb.x, hsb.y, hsb.z)); + color.set(Hsb(hsb.x, hsb.y, hsb.z)); return color; } diff --git a/Engine/source/core/color.h b/Engine/source/core/color.h index c4e1ffe13..b9da6872e 100644 --- a/Engine/source/core/color.h +++ b/Engine/source/core/color.h @@ -34,9 +34,24 @@ #include "console/engineAPI.h" #endif +#ifdef TORQUE_USE_LEGACY_GAMMA const F32 gGamma = 2.2f; const F32 gOneOverGamma = 1.f / 2.2f; +#else +const F32 gGamma = 2.4f; +const F32 gOneOverGamma = 1.f / 2.4f; const F32 gOneOver255 = 1.f / 255.f; +#endif + +struct Hsb +{ + Hsb() :hue(0), sat(0), brightness(0) {}; + Hsb(F64 h, F64 s, F64 b) :hue(h), sat(s), brightness(b) {}; + + F64 hue; ///Hue + F64 sat; ///Saturation + F64 brightness; //Brightness/Value/Lightness +}; class ColorI; @@ -55,9 +70,14 @@ public: LinearColorF(const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a = 1.0f); LinearColorF(const ColorI &color); LinearColorF(const char* pStockColorName); + LinearColorF(const Hsb& color); + + F32 srgbToLinearChannel(const F32 chan_col); + F32 linearChannelToSrgb(const F32 chan_col); void set( const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a = 1.0f ); void set( const char* pStockColorName ); + void set(const Hsb& color); static const LinearColorF& StockColor( const char* pStockColorName ); StringTableEntry StockColor( void ); @@ -88,6 +108,7 @@ public: U32 getARGBPack() const; U32 getRGBAPack() const; U32 getABGRPack() const; + Hsb getHSB() const; void interpolate(const LinearColorF& in_rC1, const LinearColorF& in_rC2, @@ -126,16 +147,6 @@ public: U8 blue; U8 alpha; - struct Hsb - { - Hsb() :hue(0), sat(0), brightness(0){}; - Hsb(F64 h, F64 s, F64 b) :hue(h), sat(s), brightness(b){}; - - F64 hue; ///Hue - F64 sat; ///Saturation - F64 brightness; //Brightness/Value/Lightness - }; - public: ColorI() : red(0), green(0), blue(0), alpha(0) {} ColorI(const ColorI& in_rCopy); @@ -247,6 +258,27 @@ public: static void destroy( void ); }; +inline F32 LinearColorF::srgbToLinearChannel(const F32 chan_col) +{ + if (chan_col < 0.0405f) { + return chan_col / 12.92f; + } + else { + return mPow((chan_col + 0.055f) / 1.055f, gGamma); + } +} + +inline F32 LinearColorF::linearChannelToSrgb(const F32 chan_col) +{ + if (chan_col <= 0.0031308f) { + return chan_col * 12.92f; + } + else { + return 1.055f * mPow(chan_col, gOneOverGamma) - 0.055f; + } +} + + //------------------------------------------------------------------------------ //-------------------------------------- INLINES (LinearColorF) // @@ -441,6 +473,72 @@ inline F32 LinearColorF::luminance() return red * 0.3f + green * 0.59f + blue * 0.11f; } +inline LinearColorF::LinearColorF(const Hsb& color) +{ + set(color); +} + +inline void LinearColorF::set(const Hsb& color) +{ + F64 c = (color.brightness / 100.0) * (color.sat / 100.0); + F64 x = c * (1.0 - fabs(fmod(color.hue / 60.0, 2.0) - 1.0)); + F64 m = (color.brightness / 100.0) - c; + + F64 r = 0.0, g = 0.0, b = 0.0; + if (color.hue < 60.0) { + r = c; g = x; b = 0.0; + } + else if (color.hue < 120.0) { + r = x; g = c; b = 0.0; + } + else if (color.hue < 180.0) { + r = 0.0; g = c; b = x; + } + else if (color.hue < 240.0) { + r = 0.0; g = x; b = c; + } + else if (color.hue < 300.0) { + r = x; g = 0.0; b = c; + } + else { + r = c; g = 0.0; b = x; + } + + red = static_cast(r + m); + green = static_cast(g + m); + blue = static_cast(b + m); + alpha = 1.0f; // Default alpha to 1.0 +} + +inline Hsb LinearColorF::getHSB() const +{ + F32 maxVal = mMax( red, mMax(green, blue)); + F32 minVal = mMin(red, mMin(green, blue)); + F32 delta = maxVal - minVal; + + Hsb hsb; + hsb.brightness = maxVal * 100.0; // Convert to percentage + hsb.sat = (maxVal > 0.0f) ? (delta / maxVal) * 100.0 : 0.0; + + if (delta > 0.0f) { + if (red == maxVal) + hsb.hue = 60.0 * mFmod(((green - blue) / delta), 6.0); + else if (green == maxVal) + hsb.hue = 60.0 * (((blue - red) / delta) + 2.0); + else + hsb.hue = 60.0 * (((red - green) / delta) + 4.0); + + if (hsb.hue < 0.0) + hsb.hue += 360.0; + } + else { + hsb.hue = 0.0; + } + + return hsb; +} + + //------------------------------------------------------------------------------ //-------------------------------------- INLINES (ColorI) // @@ -719,7 +817,7 @@ inline U16 ColorI::get4444() const U16(U16(blue >> 4) << 0)); } -inline ColorI::Hsb ColorI::getHSB() const +inline Hsb ColorI::getHSB() const { // Normalize RGB values to [0, 1] F64 rPercent = (F64)red / 255.0; @@ -740,22 +838,22 @@ inline ColorI::Hsb ColorI::getHSB() const S = delta / maxColor; // Saturation // Compute hue - if (fabs(maxColor - rPercent) < 1e-6) + if (mFabsD(maxColor - rPercent) < 1e-6) { H = 60.0 * ((gPercent - bPercent) / delta); } - else if (fabs(maxColor - gPercent) < 1e-6) + else if (mFabsD(maxColor - gPercent) < 1e-6) { H = 60.0 * (((bPercent - rPercent) / delta) + 2.0); } - else if (fabs(maxColor - bPercent) < 1e-6) + else if (mFabsD(maxColor - bPercent) < 1e-6) { H = 60.0 * (((rPercent - gPercent) / delta) + 4.0); } } // Prepare the output HSB struct - ColorI::Hsb val; + Hsb val; val.hue = H; // Round to nearest integer val.sat = S * 100.0; // Convert to percentage val.brightness = B * 100.0; // Convert to percentage @@ -782,9 +880,9 @@ inline String ColorI::getHex() const inline LinearColorF::LinearColorF( const ColorI &color) { - red = sSrgbToLinear[color.red], - green = sSrgbToLinear[color.green], - blue = sSrgbToLinear[color.blue], + red = srgbToLinearChannel(color.red * gOneOver255), + green = srgbToLinearChannel(color.green * gOneOver255), + blue = srgbToLinearChannel(color.blue * gOneOver255), alpha = F32(color.alpha * gOneOver255); } @@ -799,14 +897,14 @@ inline ColorI LinearColorF::toColorI(const bool keepAsLinear) else { #ifdef TORQUE_USE_LEGACY_GAMMA - float r = mPow(red, gOneOverGamma); - float g = mPow(green, gOneOverGamma); - float b = mPow(blue, gOneOverGamma); + F32 r = mPow(red, gOneOverGamma); + F32 g = mPow(green, gOneOverGamma); + F32 b = mPow(blue, gOneOverGamma); return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5)); #else - float r = red < 0.0031308f ? 12.92f * red : 1.055f * mPow(red, 1.0f / 2.4f) - 0.055f; - float g = green < 0.0031308f ? 12.92f * green : 1.055f * mPow(green, 1.0f / 2.4f) - 0.055f; - float b = blue < 0.0031308f ? 12.92f * blue : 1.055f * mPow(blue, 1.0f / 2.4f) - 0.055f; + F32 r = linearChannelToSrgb(red); + F32 g = linearChannelToSrgb(green); + F32 b = linearChannelToSrgb(blue); return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5f)); #endif } @@ -823,14 +921,14 @@ inline ColorI LinearColorF::toColorI(const bool keepAsLinear) else { #ifdef TORQUE_USE_LEGACY_GAMMA - float r = mPow(red, gOneOverGamma); - float g = mPow(green, gOneOverGamma); - float b = mPow(blue, gOneOverGamma); + F32 r = mPow(red, gOneOverGamma); + F32 g = mPow(green, gOneOverGamma); + F32 b = mPow(blue, gOneOverGamma); return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5)); #else - float r = red < 0.0031308f ? 12.92f * red : 1.055f * mPow(red, 1.0f / 2.4f) - 0.055f; - float g = green < 0.0031308f ? 12.92f * green : 1.055f * mPow(green, 1.0f / 2.4f) - 0.055f; - float b = blue < 0.0031308f ? 12.92f * blue : 1.055f * mPow(blue, 1.0f / 2.4f) - 0.055f; + F32 r = linearChannelToSrgb(red); + F32 g = linearChannelToSrgb(green); + F32 b = linearChannelToSrgb(blue); return ColorI(U8(r * 255.0f + 0.5f), U8(g * 255.0f + 0.5f), U8(b * 255.0f + 0.5f), U8(alpha * 255.0f + 0.5f)); #endif } diff --git a/Engine/source/gui/controls/guiColorPicker.cpp b/Engine/source/gui/controls/guiColorPicker.cpp index d313d3412..1bf3a935a 100644 --- a/Engine/source/gui/controls/guiColorPicker.cpp +++ b/Engine/source/gui/controls/guiColorPicker.cpp @@ -103,7 +103,7 @@ void GuiColorPickerCtrl::initPersistFields() void GuiColorPickerCtrl::renderBlendRange(RectI& bounds) { ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, 100, 100)); + currentColor.set(Hsb(mSelectedHue, 100, 100)); GFX->getDrawUtil()->drawRectFill(bounds, currentColor, 0.0f, ColorI(0,0,0,0), true); } @@ -123,7 +123,7 @@ void GuiColorPickerCtrl::renderBlendSelector(RectI& bounds) selectorRect.set(Point2I(selectorPos.x - mSelectorGap, selectorPos.y - mSelectorGap), Point2I(mSelectorGap * 2, mSelectorGap * 2)); ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); + currentColor.set(Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); GFX->getDrawUtil()->drawRectFill(selectorRect, currentColor, 2.0f, ColorI::WHITE); } @@ -145,8 +145,8 @@ void GuiColorPickerCtrl::renderHueGradient(RectI& bounds, U32 numColours) U32 nextHue = static_cast((F32(i + 1) / F32(numColours)) * 360.0f); ColorI currentColor, nextColor; - currentColor.set(ColorI::Hsb(currentHue, 100, 100)); - nextColor.set(ColorI::Hsb(nextHue, 100, 100)); + currentColor.set(Hsb(currentHue, 100, 100)); + nextColor.set(Hsb(nextHue, 100, 100)); switch (mSelectorMode) { @@ -226,7 +226,7 @@ void GuiColorPickerCtrl::renderHueSelector(RectI& bounds) } ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, 100, 100)); + currentColor.set(Hsb(mSelectedHue, 100, 100)); GFX->getDrawUtil()->drawRectFill(selectorRect, currentColor, 2.0f, ColorI::WHITE); } @@ -241,7 +241,7 @@ void GuiColorPickerCtrl::renderAlphaGradient(RectI& bounds) S32 b = bounds.point.y + bounds.extent.y; ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, 100, 100)); + currentColor.set(Hsb(mSelectedHue, 100, 100)); ColorI alphaCol = ColorI::BLACK; alphaCol.alpha = 0; @@ -317,7 +317,7 @@ void GuiColorPickerCtrl::renderAlphaSelector(RectI& bounds) } ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, 100, 100)); + currentColor.set(Hsb(mSelectedHue, 100, 100)); currentColor.alpha = mSelectedAlpha; GFX->getDrawUtil()->drawRectFill(selectorRect, currentColor, 2.0f, ColorI::WHITE); @@ -342,7 +342,7 @@ void GuiColorPickerCtrl::renderEyeDropper() RectI magnifierBounds(magnifierPosition, magnifierSize); ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); + currentColor.set(Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); currentColor.alpha = mSelectedAlpha; GFX->getDrawUtil()->drawRectFill(magnifierBounds, currentColor, 2.0f, ColorI::BLACK); @@ -368,7 +368,7 @@ void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect) case GuiColorPickerCtrl::pPalette: { ColorI currentColor; - currentColor.set(ColorI::Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); + currentColor.set(Hsb(mSelectedHue, mSelectedSaturation, mSelectedBrightness)); currentColor.alpha = mSelectedAlpha; GFX->getDrawUtil()->drawRectFill(boundsRect, currentColor); break; @@ -593,7 +593,7 @@ void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event) eyeDropperCap->getColor(eyeDropperPos.x, eyeDropperPos.y, sampledColor); // Convert the sampled color to HSB - ColorI::Hsb hsb = sampledColor.getHSB(); + Hsb hsb = sampledColor.getHSB(); mSelectedHue = hsb.hue; mSelectedSaturation = hsb.sat; mSelectedBrightness = hsb.brightness; @@ -779,7 +779,7 @@ DefineEngineMethod(GuiColorPickerCtrl, getSelectedAlpha, F64, (), , "Gets the cu DefineEngineMethod(GuiColorPickerCtrl, setSelectedColorI, void, (ColorI col), , "Sets the current selected hsb from a colorI value.") { - ColorI::Hsb hsb(col.getHSB()); + Hsb hsb(col.getHSB()); object->setSelectedHue(hsb.hue); object->setSelectedSaturation(hsb.sat); object->setSelectedBrightness(hsb.brightness); @@ -789,26 +789,25 @@ DefineEngineMethod(GuiColorPickerCtrl, setSelectedColorI, void, (ColorI col), , DefineEngineMethod(GuiColorPickerCtrl, getSelectedColorI, ColorI, (), , "Gets the current selected hsb as a colorI value.") { ColorI col; - col.set(ColorI::Hsb(object->getSelectedHue(), object->getSelectedSaturation(), object->getSelectedBrightness())); + col.set(Hsb(object->getSelectedHue(), object->getSelectedSaturation(), object->getSelectedBrightness())); col.alpha = object->getSelectedAlpha(); return col; } DefineEngineMethod(GuiColorPickerCtrl, setSelectedLinearColor, void, (LinearColorF colF), , "Sets the current selected hsb froma a LinearColorF value.") { - ColorI col = colF.toColorI(); - ColorI::Hsb hsb(col.getHSB()); + Hsb hsb = colF.getHSB(); object->setSelectedHue(hsb.hue); object->setSelectedSaturation(hsb.sat); object->setSelectedBrightness(hsb.brightness); - object->setSelectedAlpha(col.alpha); + object->setSelectedAlpha(colF.alpha * 255.0); } DefineEngineMethod(GuiColorPickerCtrl, getSelectedLinearColor, LinearColorF, (), , "Gets the current selected hsb as a LinearColorF value.") { - ColorI col; - col.set(ColorI::Hsb(object->getSelectedHue(), object->getSelectedSaturation(), object->getSelectedBrightness())); - col.alpha = object->getSelectedAlpha(); - return LinearColorF(col); + LinearColorF col; + col.set(Hsb(object->getSelectedHue(), object->getSelectedSaturation(), object->getSelectedBrightness())); + col.alpha = (F32)object->getSelectedAlpha() / 255.0f; + return col; } diff --git a/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui b/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui index 236262deb..e796eb46c 100644 --- a/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui +++ b/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui @@ -109,7 +109,7 @@ $guiContent = new GuiColorPickerCtrl(ColorPickerDlg,EditorGuiGroup) { position = "213 3"; extent = "50 25"; profile = "GuiDefaultProfile"; - command = "%selHue = ColorNewSelected.getSelectedHue();\n%selSat = ColorNewSelected.getSelectedSaturation();\n%selBright = ColorNewSelected.getSelectedBrightness();\n\nHueTextEditor.setText(%selHue);\nSatTextEditor.setText(%selSat);\nBrightTextEditor.setText(%selBright);\n\n%color = ColorNewSelected.getSelectedColorI();\nRedTextEdit.setText(getWord(%color, 0));\nGreenTextEdit.setText(getWord(%color, 1));\nBlueTextEdit.setText(getWord(%color, 2));\nAlphaTextEdit.setText(getWord(%color, 3));\n\n%hex = ColorRGBToHEX(%color);\nHexTextEditor.setText(%hex);"; + command = "%selHue = ColorNewSelected.getSelectedHue();\n%selSat = ColorNewSelected.getSelectedSaturation();\n%selBright = ColorNewSelected.getSelectedBrightness();\n\nHueTextEditor.setText(%selHue);\nSatTextEditor.setText(%selSat);\nBrightTextEditor.setText(%selBright);\n\n%color = $ColorCallbackType == 1 ? ColorNewSelected.getSelectedColorI() : ColorNewSelected.getSelectedLinearColor();\nRedTextEdit.setText(getWord(%color, 0));\nGreenTextEdit.setText(getWord(%color, 1));\nBlueTextEdit.setText(getWord(%color, 2));\nAlphaTextEdit.setText(getWord(%color, 3));\n\n%hex = ColorRGBToHEX(ColorNewSelected.getSelectedColorI());\nHexTextEditor.setText(%hex);"; tooltipProfile = "GuiToolTipProfile"; }; new GuiColorPickerCtrl(ColorOld) { @@ -437,8 +437,12 @@ function ColorPickerRGBClass::onValidate(%this) %alpha = AlphaTextEdit.getValue(); //Update all the other color fields - %hsb = ColorRGBToHSB(%red SPC %green SPC %blue); - + if( $ColorCallbackType == 1) + %hsb = ColorRGBToHSB(%red SPC %green SPC %blue); + else + { + %hsb = ColorLinearRGBToHSB(%red SPC %green SPC %blue); + } // these automatically update our ColorNewSelected which // updates all text fields including these. ColorHueRange.setSelectedHue(getWord(%hsb, 0)); @@ -448,7 +452,7 @@ function ColorPickerRGBClass::onValidate(%this) ColorBlendRange.setSelectedBrightness(getWord(%hsb, 2)); ColorBlendRange.executeUpdate(); - ColorAlphaRange.setSelectedAlpha(%alpha); + ColorAlphaRange.setSelectedAlpha(%alpha * 255.0); ColorAlphaRange.executeUpdate(); }