From 704e304eef8cc1076827d2a3e674b9ec746be49e Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 23 Jan 2025 19:06:35 +0000 Subject: [PATCH] display correct values Few fixes to display float values if the inspector field asks for floats Split Hsb out from colorI now linearColorF can return HSB from its own color without having to go through the colorI conversion, hopefully gets rid of rounding errors etc since we are only doing the calc once. --- Engine/source/console/consoleFunctions.cpp | 18 +- Engine/source/core/color.h | 158 ++++++++++++++---- Engine/source/gui/controls/guiColorPicker.cpp | 37 ++-- .../game/tools/gui/colorPicker.ed.gui | 12 +- 4 files changed, 170 insertions(+), 55 deletions(-) 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(); }