Android Question Get contrast color for (partially) transparent label

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Using the code in this thread:

Trying to get the contrasting text colour for a (partially) transparent label where the parent is a non-transparent panel.
In the great majority the above code gets the right contrasting colour but with lower values for the A of ARGB the contrasting colour can
be wrong, mainly (only?) giving white when it should be black.

For example:
Panel colour: ARGB 255, 255, 255, 100
Label colour: ARGB 10, 150, 150, 150

This will give the contrasting colour white, but clearly this should be black.

How should this be coded to get the right contrasting colour?

RBS
 

JohnC

Expert
Licensed User
Longtime User
ChatGPT says:

Your issue seems to stem from not accounting for the alpha (transparency) component of the colors when calculating contrast. In your given example, the panel color has full opacity while the label color is highly transparent, affecting how the underlying panel color influences the perceived color of the label.

To solve this problem, you need to blend the label color with the panel color based on the alpha value of the label color before calculating the luminance and contrast. Here's an approach to modify your Luminance method to include the effect of the panel's background color when the label color is transparent:

  1. Blend the Label Color with the Panel Color: Calculate the effective color of the label by blending it with the panel's color based on the label's alpha value. This is necessary because the visual appearance of the label will be influenced by the color underneath it due to its transparency.
  2. Calculate Luminance of the Effective Color: Use this blended color to calculate the luminance instead of the original label color.
  3. Adjust the Contrast Calculation if Needed: Your existing contrast calculation method may remain the same if it already accurately calculates contrast based on luminance. Ensure that it uses the adjusted luminance that accounts for transparency.
Here's an outline of how you might adjust the code, focusing on blending the label's color with the panel's color before calculating luminance:
B4X:
' Blends two colors based on the alpha value of the top color
Private Sub BlendColors(topColor As Int, bottomColor As Int) As Int()
    Dim topARGB() As Int = GetARGB(topColor)
    Dim bottomARGB() As Int = GetARGB(bottomColor)
    Dim blended(3) As Int
    Dim alphaTop As Float = topARGB(0) / 255.0
    Dim inverseAlphaTop As Float = 1 - alphaTop
    
    For i = 1 To 3
        blended(i) = topARGB(i) * alphaTop + bottomARGB(i) * inverseAlphaTop
    Next
    
    Return blended ' Return the blended RGB values; Alpha is ignored for luminance calculation
End Sub

' Modified Luminance function to accept RGB values directly
Private Sub LuminanceFromRGB(rgb() As Int) As Float
    Dim a(3) As Float
    For i = 1 To 3
        Dim v As Float = rgb(i) / 255
        If v <= 0.03928 Then
            a(i - 1) = v / 12.92
        Else
            a(i - 1) = Power((v + 0.055) / 1.055, 2.4)
        End If
    Next
    Return a(0) * 0.2126 + a(1) * 0.7152 + a(2) * 0.0722
End Sub

' Adjust your button click event or wherever you calculate contrast
Sub Button1_Click
    ' Example colors (These would be dynamically determined in your actual code)
    Dim labelColor As Int = 0x0A969696 ' ARGB 10, 150, 150, 150
    Dim panelColor As Int = 0xFFFFFFFF ' ARGB 255, 255, 255, 255
    
    ' Blend the label color with the panel color
    Dim blendedLabelRGB() As Int = BlendColors(labelColor, panelColor)
    
    ' Calculate luminance of the blended color
    Dim lumLabel As Float = LuminanceFromRGB(blendedLabelRGB)
    Dim lumPanel As Float = Luminance(panelColor) ' Panel color is already opaque, so direct luminance calculation is fine
    
    ' Continue with contrast calculation as before
    Dim contrastRatio As Float = ContrastUsingLuminance(lumLabel, lumPanel)
    Log(contrastRatio)
End Sub
This example outlines how you might adjust your code to blend the colors and calculate luminance based on the effective color after blending. Be sure to integrate and adjust it according to the rest of your project's structure and logic.
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
ChatGPT says:

Your issue seems to stem from not accounting for the alpha (transparency) component of the colors when calculating contrast. In your given example, the panel color has full opacity while the label color is highly transparent, affecting how the underlying panel color influences the perceived color of the label.

To solve this problem, you need to blend the label color with the panel color based on the alpha value of the label color before calculating the luminance and contrast. Here's an approach to modify your Luminance method to include the effect of the panel's background color when the label color is transparent:


  1. Blend the Label Color with the Panel Color: Calculate the effective color of the label by blending it with the panel's color based on the label's alpha value. This is necessary because the visual appearance of the label will be influenced by the color underneath it due to its transparency.
  2. Calculate Luminance of the Effective Color: Use this blended color to calculate the luminance instead of the original label color.
  3. Adjust the Contrast Calculation if Needed: Your existing contrast calculation method may remain the same if it already accurately calculates contrast based on luminance. Ensure that it uses the adjusted luminance that accounts for transparency.
Here's an outline of how you might adjust the code, focusing on blending the label's color with the panel's color before calculating luminance:
B4X:
' Blends two colors based on the alpha value of the top color
Private Sub BlendColors(topColor As Int, bottomColor As Int) As Int()
    Dim topARGB() As Int = GetARGB(topColor)
    Dim bottomARGB() As Int = GetARGB(bottomColor)
    Dim blended(3) As Int
    Dim alphaTop As Float = topARGB(0) / 255.0
    Dim inverseAlphaTop As Float = 1 - alphaTop
   
    For i = 1 To 3
        blended(i) = topARGB(i) * alphaTop + bottomARGB(i) * inverseAlphaTop
    Next
   
    Return blended ' Return the blended RGB values; Alpha is ignored for luminance calculation
End Sub

' Modified Luminance function to accept RGB values directly
Private Sub LuminanceFromRGB(rgb() As Int) As Float
    Dim a(3) As Float
    For i = 1 To 3
        Dim v As Float = rgb(i) / 255
        If v <= 0.03928 Then
            a(i - 1) = v / 12.92
        Else
            a(i - 1) = Power((v + 0.055) / 1.055, 2.4)
        End If
    Next
    Return a(0) * 0.2126 + a(1) * 0.7152 + a(2) * 0.0722
End Sub

' Adjust your button click event or wherever you calculate contrast
Sub Button1_Click
    ' Example colors (These would be dynamically determined in your actual code)
    Dim labelColor As Int = 0x0A969696 ' ARGB 10, 150, 150, 150
    Dim panelColor As Int = 0xFFFFFFFF ' ARGB 255, 255, 255, 255
   
    ' Blend the label color with the panel color
    Dim blendedLabelRGB() As Int = BlendColors(labelColor, panelColor)
   
    ' Calculate luminance of the blended color
    Dim lumLabel As Float = LuminanceFromRGB(blendedLabelRGB)
    Dim lumPanel As Float = Luminance(panelColor) ' Panel color is already opaque, so direct luminance calculation is fine
   
    ' Continue with contrast calculation as before
    Dim contrastRatio As Float = ContrastUsingLuminance(lumLabel, lumPanel)
    Log(contrastRatio)
End Sub
This example outlines how you might adjust your code to blend the colors and calculate luminance based on the effective color after blending. Be sure to integrate and adjust it according to the rest of your project's structure and logic.
I can see that this is the right principle, that is somehow blend the foreground colour and the background colour, based on the transparency of the foreground colour.
The code as provided doesn't work (even after adjusting from 1-based to 0-based arrays), but I think I can work this out myself, probably by just mixing the luminances
of the foreground and background colour.
Will report back if I have it right.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
I can see that this is the right principle, that is somehow blend the foreground colour and the background colour, based on the transparency of the foreground colour.
The code as provided doesn't work (even after adjusting from 1-based to 0-based arrays), but I think I can work this out myself, probably by just mixing the luminances
of the foreground and background colour.
Will report back if I have it right.

RBS
I think this works OK:
It presumes that the top view may have transparency but the parent view has not.

B4X:
Sub GetARGB(Color As Int) As Int()
    
    Dim res(4) As Int
    
    res(0) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff000000), 24)
    res(1) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff0000), 16)
    res(2) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff00), 8)
    res(3) = Bit.And(Color, 0xff)
    
    Return res
    
End Sub

Sub GetContrastingColourFromTopAndBottomColour(iTopColor As Int, iBottomColor As Int) As Int
    
    Dim i As Int
    Dim arrARGB_Top() As Int
    Dim arrARGB_Bottom() As Int
    Dim arrRGB_Top(3) As Int
    Dim arrRGB_Bottom(3) As Int
    Dim iAlpha As Int
    Dim fLuminanceTop As Float
    Dim fLuminanceBottom As Float
    Dim fLuminanceFinal As Float
    
    arrARGB_Top = GetARGB(iTopColor)
    arrARGB_Bottom = GetARGB(iBottomColor)
    iAlpha = arrARGB_Top(0)
    
    For i = 0 To 2
        arrRGB_Top(i) = arrARGB_Top(i + 1)
        arrRGB_Bottom(i) = arrARGB_Bottom(i + 1)
    Next
    
    fLuminanceTop = LuminanceFromRGB(arrRGB_Top)
    fLuminanceBottom = LuminanceFromRGB(arrRGB_Bottom)
    fLuminanceFinal = (iAlpha/ 255) * fLuminanceTop + ((255 - iAlpha) / 255) * fLuminanceBottom
    
    If fLuminanceFinal > 0.5 Then
        Return xui.Color_Black
    Else
        Return xui.Color_White
    End If
    
End Sub

Private Sub LuminanceFromRGB(rgb() As Int) As Float
    
    Dim i As Int
    Dim a(3) As Float
    
    For i = 0 To 2
        Dim v As Float = rgb(i) / 255
        If v <= 0.03928 Then
            a(i) = v / 12.92
        Else
            a(i) = Power((v + 0.055) / 1.055, 2.4)
        End If
    Next
    
    Return a(0) * 0.2126 + a(1) * 0.7152 + a(2) * 0.0722
    
End Sub
[CODE]

Will test some further.

RBS
 
Upvote 0
Top