Results 1 to 14 of 14

Thread: RGB to HSB with *Byte* values??

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,837

    RGB to HSB with *Byte* values??

    I'm trying to convert a standard RGB color value to HSB as understood by the Windows Ribbon. The macro is:

    Code:
    typedef DWORD UI_HSBCOLOR;
    __inline UI_HSBCOLOR UI_HSB(BYTE hue, BYTE saturation, BYTE brightness)
    {
        return hue | (saturation << 8) | (brightness << 16);
    }
    The problem is every conversion method I can find is based on float (Single) between 0 and 1.0, while obviously a Byte is 0-255.

    There's a Windows API to convert to HSL (ColorRGBToHLS), but that's apparently different. And every conversion method for HSL to HSB is also based on float.

    The documentation is also useless and uses floats... https://learn.microsoft.com/en-us/wi...ert-rgb-to-hsb
    It's beyond obnoxious that their documentation is basically "you have to use photo editing software to find hard coded hex values".

    Any idea how to properly convert RGB to HSB based on 0-255 Byte values?

  2. #2
    PowerPoster VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    2,286

    Re: RGB to HSB with *Byte* values??

    Copilot says:

    To convert HSB (Hue, Saturation, Brightness) values from floats (ranging from 0.0 to 1.0) to bytes (ranging from 0 to 255) without converting to RGB, you simply need to scale the float values to the byte range.

    Conversion Formula

    byte byteValue = (byte)(floatValue * 255);

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,837

    Re: RGB to HSB with *Byte* values??

    Copilot says is a step up from drunk schizophrenic ranting on corner says, but only barely

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,837

    Re: RGB to HSB with *Byte* values??

    Ok I found a C++ saple; can someone better with the language double check I got it right? Especially the differences in how division behaves?

    Code:
        ' void CTheme::RGBToHSB(COLORREF rgb, BYTE& hue, BYTE& saturation, BYTE& brightness)
        Private Sub RGBToHSB(ByVal rgb As Long, hue As Byte, saturation As Byte, brightness As Byte)
        ' {
            ' BYTE r = GetRValue(rgb);
            Dim r As Byte = GetRValue(rgb)
            ' BYTE g = GetGValue(rgb);
            Dim g As Byte = GetGValue(rgb)
            ' BYTE b = GetBValue(rgb);
            Dim b As Byte = GetBValue(rgb)
            ' BYTE minRGB = min(min(r, g), b);
            Dim minRGB As Byte = min(min(r, g), b)
            ' BYTE maxRGB = max(max(r, g), b);
            Dim maxRGB As Byte = max(max(r, g), b)
            ' BYTE delta = maxRGB - minRGB;
            Dim delta As Byte = maxRGB - minRGB
            ' double l = maxRGB;
            Dim l As Double = maxRGB
            ' double s = 0.0;
            Dim s As Double = 0.0
            ' double h = 0.0;
            Dim h As Double = 0.0
            ' if (maxRGB == 0)
            ' {
                ' hue = 0;
                ' saturation = 0;
                ' brightness = 0;
                ' return;
            ' }
            If maxRGB = 0 Then
                hue = 0
                saturation = 0
                brightness = 0
                Exit Sub
            End If
            ' if (maxRGB)
                ' s = (255.0 * delta) / maxRGB;
            If (maxRGB) Then s = (255.0 * delta) \ maxRGB
            ' if (static_cast<BYTE>(s) != 0)
            ' {
            If CByte(s) <> 0 Then
                ' if (r == maxRGB)
                    ' h = 0 + 43 * static_cast<double>(g - b) / delta;
                    If (r = maxRGB) Then
                        h = 0 + 43 * CDbl(g - b) \ delta
                ' else if (g == maxRGB)
                    ' h = 85 + 43 * static_cast<double>(b - r) / delta;
                    ElseIf (g = maxRGB) Then
                       h = 85 + 43 * CDbl(b - r) \ delta
                ' else if (b == maxRGB)
                    ' h = 171 + 43 * static_cast<double>(r - g) / delta;
                    ElseIf (b = maxRGB) Then
                      h = 171 + 43 * CDbl(r - g) \ delta
                    End If
            ' }
            ' else
                ' h = 0.0;
            Else
                h = 0.0
            End If
            ' hue = static_cast<BYTE>(h);
            ' saturation = static_cast<BYTE>(s);
            ' brightness = static_cast<BYTE>(l);
            hue = CByte(h)
            saturation = CByte(s)
            brightness = CByte(l)
            
        ' }
        End Sub
    'helper macros:
    Public Function min(ByVal x As Long, ByVal y As Long) As Long: Return If(x < y, x, y): End Function
    Public Function max(ByVal x As Long, ByVal y As Long) As Long: Return If(x > y, x, y): End Function
    Public Function GetRValue(ByVal rgb As Long) As Long: Return RGB_R(rgb): End Function
    Public Function GetGValue(ByVal rgb As Long) As Long: Return RGB_G(rgb): End Function
    Public Function GetBValue(ByVal rgb As Long) As Long: Return RGB_B(rgb): End Function
    Last edited by fafalone; Feb 5th, 2025 at 06:02 AM.

  5. #5
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,738

    Re: RGB to HSB with *Byte* values??

    Personally, I was going to say precisely what CoPilot said. In VB6 terms:

    Code:
    Dim bbH as Byte
    bbH = CByte(fH * 255&)
    Where fH is a float (single or double) from 0 to 1 (inclusive) that represents HUE. You'd do the same for Saturation and Lightness or Brightness. This will work for either HSL or HSB values that are 0 to 1.

    I've done precisely this many times through the years.

    One trick is to realize that, when represented from 0 to 1, the 1 is included, so we must multiply by 255, not 256. That's a common mistake.



    To convert back to the 0 to 1 range, simply divide by 255! (a float), with the / operator.



    Also, I'm not sure I've ever seen either HSL or HSB represented as hex. With respect to colors, that seems to be almost exclusively an RGB thing. However, one does have to be careful as to whether or not it's represented as Code-BGR (as it is for OLE colors), ABGR, BGR, ARGB, or RGB, which are all other alternative hex representations in various situations.
    Last edited by Elroy; Feb 5th, 2025 at 08:18 AM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,788

    Re: RGB to HSB with *Byte* values??

    Do you want to use purely integer operations converting RGB with byte components to HSB with byte components or are you ok using floats internally to do the conversion and as a final step convert the FP results to bytes?

    As already mention you can very easily "quantize" the floats when they are normalized (i.e. 0 to 1 range) by simply multiplying with 255 which should be obvious IMO.

    Note: They used 240 as upper bound in ColorRGBToHLS API which is beyond comprehension but probably has some reasoning behind this choice.

    cheers,
    </wqw>

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,837

    Re: RGB to HSB with *Byte* values??

    I don't mind using float/double internally so long as rounding/precision issues don't mess with the final result. I'll probably rig up something to test the code I ported if no one has a proven solution. Just thought of maybe looking around PhotoDemon's source for something too ... Good chance it has related code .

  8. #8
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,788

    Re: RGB to HSB with *Byte* values??

    I'm personally using this

    Code:
    Private Declare Function OleTranslateColor Lib "oleaut32" (ByVal lOleColor As Long, ByVal lHPalette As Long, ByVal lColorRef As Long) As Long
    
    Private Type UcsRgbQuad
        R                   As Byte
        G                   As Byte
        B                   As Byte
        A                   As Byte
    End Type
    
    Public Function RGBToHSB(ByVal clrValue As OLE_COLOR) As Variant
        Const IDX_HUE       As Long = 0
        Const IDX_SAT       As Long = 1
        Const IDX_BRI       As Long = 2
        Dim nTemp           As Double
        Dim lMin            As Long
        Dim lMax            As Long
        Dim lDelta          As Long
        Dim rgbValue        As UcsRgbQuad
        Dim vRetVal         As Variant
      
        ReDim vRetVal(0 To 2) As Double
        Call OleTranslateColor(clrValue, 0, VarPtr(rgbValue))
        lMax = IIf(rgbValue.R > rgbValue.G, IIf(rgbValue.R > rgbValue.B, rgbValue.R, rgbValue.B), IIf(rgbValue.G > rgbValue.B, rgbValue.G, rgbValue.B))
        lMin = IIf(rgbValue.R < rgbValue.G, IIf(rgbValue.R < rgbValue.B, rgbValue.R, rgbValue.B), IIf(rgbValue.G < rgbValue.B, rgbValue.G, rgbValue.B))
        lDelta = lMax - lMin
        vRetVal(IDX_BRI) = (lMax * 100) / 255
        If lMax > 0 Then
            vRetVal(IDX_SAT) = (lDelta / lMax) * 100
            If lDelta > 0 Then
                If lMax = rgbValue.R Then
                    nTemp = (CLng(rgbValue.G) - rgbValue.B) / lDelta
                ElseIf lMax = rgbValue.G Then
                    nTemp = 2 + (CLng(rgbValue.B) - rgbValue.R) / lDelta
                Else
                    nTemp = 4 + (CLng(rgbValue.R) - rgbValue.G) / lDelta
                End If
                vRetVal(IDX_HUE) = nTemp * 60
                If vRetVal(IDX_HUE) < 0 Then
                    vRetVal(IDX_HUE) = vRetVal(IDX_HUE) + 360
                End If
            End If
        End If
        RGBToHSB = vRetVal
    End Function
    . . . and store the result as FP tripple.

    cheers,
    </wqW>

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,738

    Re: RGB to HSB with *Byte* values??

    Quote Originally Posted by wqweto View Post
    Note: They used 240 as upper bound in ColorRGBToHLS API which is beyond comprehension but probably has some reasoning behind this choice.
    That's an excellent point. In some contexts, 240 is the upper bound (and multiple for 0 to 1 to integer) for "brightness" for HSB, and for "lightness" for HSL. In other contexts, when represented as an integer, it might be 0 to 100 (so 100 would be the multiple). So, you have to look at the specifications where HSB or HSL is needed to know for sure.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,837

    Re: RGB to HSB with *Byte* values??

    Quote Originally Posted by wqweto View Post
    I'm personally using this

    Code:
    Private Declare Function OleTranslateColor Lib "oleaut32" (ByVal lOleColor As Long, ByVal lHPalette As Long, ByVal lColorRef As Long) As Long
    
    Private Type UcsRgbQuad
        R                   As Byte
        G                   As Byte
        B                   As Byte
        A                   As Byte
    End Type
    
    Public Function RGBToHSB(ByVal clrValue As OLE_COLOR) As Variant
        Const IDX_HUE       As Long = 0
        Const IDX_SAT       As Long = 1
        Const IDX_BRI       As Long = 2
        Dim nTemp           As Double
        Dim lMin            As Long
        Dim lMax            As Long
        Dim lDelta          As Long
        Dim rgbValue        As UcsRgbQuad
        Dim vRetVal         As Variant
      
        ReDim vRetVal(0 To 2) As Double
        Call OleTranslateColor(clrValue, 0, VarPtr(rgbValue))
        lMax = IIf(rgbValue.R > rgbValue.G, IIf(rgbValue.R > rgbValue.B, rgbValue.R, rgbValue.B), IIf(rgbValue.G > rgbValue.B, rgbValue.G, rgbValue.B))
        lMin = IIf(rgbValue.R < rgbValue.G, IIf(rgbValue.R < rgbValue.B, rgbValue.R, rgbValue.B), IIf(rgbValue.G < rgbValue.B, rgbValue.G, rgbValue.B))
        lDelta = lMax - lMin
        vRetVal(IDX_BRI) = (lMax * 100) / 255
        If lMax > 0 Then
            vRetVal(IDX_SAT) = (lDelta / lMax) * 100
            If lDelta > 0 Then
                If lMax = rgbValue.R Then
                    nTemp = (CLng(rgbValue.G) - rgbValue.B) / lDelta
                ElseIf lMax = rgbValue.G Then
                    nTemp = 2 + (CLng(rgbValue.B) - rgbValue.R) / lDelta
                Else
                    nTemp = 4 + (CLng(rgbValue.R) - rgbValue.G) / lDelta
                End If
                vRetVal(IDX_HUE) = nTemp * 60
                If vRetVal(IDX_HUE) < 0 Then
                    vRetVal(IDX_HUE) = vRetVal(IDX_HUE) + 360
                End If
            End If
        End If
        RGBToHSB = vRetVal
    End Function
    . . . and store the result as FP tripple.

    cheers,
    </wqW>
    Yeah there's no correlation at all for most colors using this...

    Code:
    Dim hr As Long = SetRibbonColors(pFramework, pbClrTheme.BackColor, pbClrStd.BackColor, pbClrHighlight.BackColor)
    
       Public Function SetRibbonColors(ByVal pFramework As IUIFramework, ByVal clrBackground As OLE_COLOR, ByVal clrText As OLE_COLOR, ByVal clrHighlight As OLE_COLOR) As Long
            If pFramework IsNot Nothing Then
                Dim pStore As IPropertyStore
                Set pStore = pFramework
                
                Dim v As Variant
                
                Dim hsbBackground As Long
                v = RGBToHSB(clrBackground)
                hsbBackground = UI_HSB(v(0), v(1), v(2))
                
                Dim hsbHighlight As Long
                v = RGBToHSB(clrHighlight)
                hsbHighlight = UI_HSB(v(0), v(1), v(2))
                
                Dim hsbText As Long
                v = RGBToHSB(clrText)
                hsbText = UI_HSB(v(0), v(1), v(2))
                
                                       
                Dim propvarBackground As Variant
                InitPropVariantFromUInt32 hsbBackground, propvarBackground
                Dim propvarHighlight As Variant
                InitPropVariantFromUInt32 hsbHighlight, propvarHighlight
                Dim propvarText As Variant
                InitPropVariantFromUInt32 hsbText, propvarText
                
                pStore.SetValue(UI_PKEY_GlobalBackgroundColor, propvarBackground)
                pStore.SetValue(UI_PKEY_GlobalTextColor, propvarText)
                pStore.SetValue(UI_PKEY_GlobalHighlightColor, propvarHighlight)
                pStore.Commit()
                Return Err.LastHresult
            End If
        End Function
        
        Private Function RGBToHSB(ByVal clrValue As OLE_COLOR) As Variant
            Const IDX_HUE       As Long = 0
            Const IDX_SAT       As Long = 1
            Const IDX_BRI       As Long = 2
            Dim nTemp           As Double
            Dim lMin            As Long
            Dim lMax            As Long
            Dim lDelta          As Long
            Dim rgbValue        As RGBQUAD
            Dim vRetVal         As Variant
      
            ReDim vRetVal(0 To 2) As Double
            Call OleTranslateColor(clrValue, 0, ByVal VarPtr(rgbValue))
            lMax = IIf(rgbValue.rgbRed > rgbValue.rgbGreen, IIf(rgbValue.rgbRed > rgbValue.rgbBlue, rgbValue.rgbRed, rgbValue.rgbBlue), IIf(rgbValue.rgbGreen > rgbValue.rgbBlue, rgbValue.rgbGreen, rgbValue.rgbBlue))
            lMin = IIf(rgbValue.rgbRed < rgbValue.rgbGreen, IIf(rgbValue.rgbRed < rgbValue.rgbBlue, rgbValue.rgbRed, rgbValue.rgbBlue), IIf(rgbValue.rgbGreen < rgbValue.rgbBlue, rgbValue.rgbGreen, rgbValue.rgbBlue))
            lDelta = lMax - lMin
            vRetVal(IDX_BRI) = (lMax * 100) / 255
            If lMax > 0 Then
                vRetVal(IDX_SAT) = (lDelta / lMax) * 100
                If lDelta > 0 Then
                    If lMax = rgbValue.rgbRed Then
                        nTemp = (CLng(rgbValue.rgbGreen) - rgbValue.rgbBlue) / lDelta
                    ElseIf lMax = rgbValue.rgbGreen Then
                        nTemp = 2 + (CLng(rgbValue.rgbBlue) - rgbValue.rgbRed) / lDelta
                    Else
                        nTemp = 4 + (CLng(rgbValue.rgbRed) - rgbValue.rgbGreen) / lDelta
                    End If
                    vRetVal(IDX_HUE) = nTemp * 60
                    If vRetVal(IDX_HUE) < 0 Then
                        vRetVal(IDX_HUE) = vRetVal(IDX_HUE) + 360
                    End If
                End If
            End If
            RGBToHSB = vRetVal
        End Function
    Not sure how to check whether it's the ribbon or the values though.

  11. #11
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,788

    Re: RGB to HSB with *Byte* values??

    RGBToHSB returns B and S in 0..100.0 range and H in 0..360.0 range so you have to normalize in UI_HSB (or other) helper all three to 0..255 (esp. hue) to not produce psychedelic colors.

    cheers,
    </wqw>

  12. #12
    Member
    Join Date
    Jul 2015
    Location
    south bavaria germany
    Posts
    37

    Re: RGB to HSB with *Byte* values??

    Thanks for the code I have a module where I collect color conversion stuff, all I come across. However HSB is still missing. With your permission I hope this is OK for you, so I will integrate that too
    MColor

  13. #13
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,779

    Re: RGB to HSB with *Byte* values??

    Quote Originally Posted by fafalone View Post
    The problem is every conversion method I can find is based on float (Single) between 0 and 1.0, while obviously a Byte is 0-255.
    Yea, I encountered this when working on this program. I had to shed some naivete about how colours worked. By the end of that project I had learned that this 0-255 scheme I was comfortable with was actually quite clumsy. It turns out that it is far more useful to think of colours in terms of percentages. This is why you keep encountering the use of Singles in colour algorithms. HSB and HSL are percentage based colour schemes. For example a saturation value of 0.7 means the colour is 70% saturated. The same goes for brightness in HSB. Only hue isn't a percentage value, it's a point of a colour wheel. Even RGB can be interpreted as a percentage based colour scheme. A red value of 0.2 means 20% red.

    My advice would be to go with the flow. Work with the singles and when it comes time to show something on the screen or any other situation where you require the tradition 0-255 scheme, you simple multiply your percentages by 255. Note though that this only works with RGB. HSL and HSB must stay as percentages as it doesn't make sense interpreting them any other way.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  14. #14
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,779

    Re: RGB to HSB with *Byte* values??

    Quote Originally Posted by Elroy View Post
    Also, I'm not sure I've ever seen either HSL or HSB represented as hex. With respect to colors, that seems to be almost exclusively an RGB thing.
    Like I said above, it wouldn't make sense. HSL and HSB specifically tell you how much a colour is saturated or how bright it is based on percentages. Even RGB is percentage based. I think the only reason RGB uses the 0-255 range is because far easier to work with integers when it comes time to perform bitwise operations on images. Floating point numbers have a more complicated representation in memory than integers so it would complicate XOR or AND operations on images. Outside of bitwise operations, you will find it a lot more straightforward to use percentages to perform certain calculations on colours.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width