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.
This commit is contained in:
marauder2k7 2025-01-23 19:06:35 +00:00
parent 48ca98ca84
commit 704e304eef
4 changed files with 170 additions and 55 deletions

View file

@ -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;
}

View file

@ -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<F32>(r + m);
green = static_cast<F32>(g + m);
blue = static_cast<F32>(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
}

View file

@ -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<U32>((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;
}

View file

@ -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();
}