B4J Question Which settings for BitmapCreator will get me what I need?

William Lancee

Well-Known Member
Licensed User
Longtime User
BitmapCreator has several optimization strategies. Therefore the following code produces results that are not what I need for my project.
Are there settings that will produce what I need. If not, are there any other ways of getting the exact ARGB pixel color from a B4XBitmap?

B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Dim rx As B4XRect
    rx.Initialize(0, 0, Root.Width, Root.Height)
    Dim cv As B4XCanvas
    cv.Initialize(Root)
 
    Dim testCol() As Int = Array As Int(0, 5, 10, 15, 20, 25, 26, 45, 254, 255)
    For i = 0 To testCol.Length - 1
        cv.ClearRect(rx)
        cv.drawRect(rx, xui.Color_ARGB(testCol(i), 0, 0, 100), True, 0)
        Dim bc As BitmapCreator
        bc.Initialize(rx.Width, rx.height)
        bc.CopyPixelsFromBitmap(cv.CreateBitmap)
        Dim argb As ARGBColor
        bc.GetARGB(200, 200, argb)
        Log(argb.a & TAB & argb.r & TAB & argb.g & TAB & argb.b)
    Next
'Log output    
    '0    0    0    0            'need: 0    0    0    100
    '5    0    0    50            'need: 5    0    0    100
    '10    0    0    76            'need: 10    0    0    100
    '15    0    0    84            'need: 15    0    0    100
    '20    0    0    89            'need: 20    0    0    100
    '25    0    0    91            'need: 25    0    0    100
    '26    0    0    98            'need: 26    0    0    100
    '45    0    0    96            'need: 45    0    0    100
    '254    0    0    99        'need: 254    0    0    100
    '255    0    0    100        'correct
End Sub
 

kimstudio

Active Member
Licensed User
Longtime User
This is because using cv.drawRect with a non-255 alpha value, RGB values like 100 is blending with background color with alpha, so it changes each time due to alpha value changes.

cv.ClearRect also matters as we need to know what color including alpha it uses to clear the rect as the background color.
 
Upvote 0

kimstudio

Active Member
Licensed User
Longtime User
I'm curious on why these values are got that seems not align with alpha blending formula. After cv.ClearRect, if the cv color is ARGB(0,0,0,0) I assume, then cv.drawRect(rx, xui.Color_ARGB(5, 0, 0, 100), True, 0) should make the color A: 5, B: 100 if we also consider the alpha of background, if not consider background alpha then we should get B: 2 = 5/255*100+(1-5/255)*0.

I think the strange color is coming from cv.CreateBitmap. @Erel could tell us how it works to convert cv to bitmap regarding colors, for instance, whether the background color of panel/mainform is also considered in saving bitmap, or there are some other color conversions.
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
Thank you for replying.
It is not related to the canvas, if I remove the canvas and use the BitmapCreator object for everything I get the same results.

B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.Color = xui.Color_Transparent
    Dim rx As B4XRect
    rx.Initialize(0, 0, Root.Width, Root.Height)
    Dim bc As BitmapCreator
    bc.Initialize(rx.Width, rx.height)

    Dim testCol() As Int = Array As Int(0, 5, 10, 15, 20, 25, 26, 45, 254, 255)
    For i = 0 To testCol.Length - 1
        bc.drawRect(rx, xui.Color_ARGB(testCol(i), 0, 0, 100), True, 0)
        Dim argb As ARGBColor
        bc.GetARGB(200, 200, argb)
        Log(argb.a & TAB & argb.r & TAB & argb.g & TAB & argb.b)
    Next
End Sub
 
Upvote 0

kimstudio

Active Member
Licensed User
Longtime User
You are so correct.

With following test we can see the problem is caused by if alpha value is not 255 in BitmapCreator:
B4X:
    Dim bc As BitmapCreator
    bc.Initialize(cvs.Width, cvs.Height)
    Dim argb As ARGBColor
    argb.a = 5
    argb.r = 0
    argb.g = 0
    argb.b = 100
    For y = 0 To cvs.Height - 1
        For x = 0 To cvs.Width - 1
            bc.SetARGB(x, y, argb)
        Next
    Next
    Dim argb As ARGBColor
    bc.GetARGB(8, 8, argb)
    Log(argb.a & TAB & argb.r & TAB & argb.g & TAB & argb.b)
'output is:    
'5    0    0    50
'should be: 
'5    0    0    100

I think there are float to int conversion errors leading to this problem. @Erel
 
Upvote 0

kimstudio

Active Member
Licensed User
Longtime User
@William Lancee BitmapCreator.bas is opensource and available. From followig code we can see why these values are got:

B4X:
'Sets the color of the specified point.
Public Sub SetARGB(x As Int, y As Int, ARGB As ARGBColor)
    SetPremultipliedColor(x, y, ARGBToPremultipliedColor(ARGB, tempPM))
End Sub

'Sets the color of the specified point. 
Public Sub SetPremultipliedColor (x As Int, y As Int, Premultiplied As PremultipliedColor)
    Dim cp As Int = x * 4 + y * mWidth * 4
    #if B4i
    Bit.FastArraySetByte(mBuffer, cp + ri, Premultiplied.r)
    Bit.FastArraySetByte(mBuffer, cp + gi, Premultiplied.g)
    Bit.FastArraySetByte(mBuffer, cp + bi, Premultiplied.b)
    Bit.FastArraySetByte(mBuffer, cp + ai, Premultiplied.a)
    #else
    mBuffer(cp + ri) = Premultiplied.r
    mBuffer(cp + gi) = Premultiplied.g
    mBuffer(cp + bi) = Premultiplied.b
    mBuffer(cp + ai) = Premultiplied.a
    #End If
    
End Sub

'Converts an ARGB color to PremultipliedColor.
'The Result parameter will hold the output.
Public Sub ARGBToPremultipliedColor (ARGB As ARGBColor, PM As PremultipliedColor) As PremultipliedColor
    Dim a As Float = ARGB.a / 255
    PM.a = ARGB.a
    PM.r = ARGB.r * a
    PM.g = ARGB.g * a
    PM.b = ARGB.b * a
    Return PM
End Sub

'Gets the color of the given point as an ARGB color.
'The Result parameter stores the output.
Public Sub GetARGB (x As Int, y As Int, Result As ARGBColor) As ARGBColor
    Dim cp As Int = x * 4 + y * mWidth * 4
#if B4i
    Result.a = Bit.FastArrayGetByte(mBuffer, cp + ai)
    Dim af As Float = Result.a / 255
    Result.r = Bit.FastArrayGetByte(mBuffer, cp + ri) / af
    Result.g = Bit.FastArrayGetByte(mBuffer, cp + gi) / af
    Result.b = Bit.FastArrayGetByte(mBuffer, cp + bi) / af
#Else
    Result.a = Bit.And(0xff, mBuffer(cp + ai))
    Dim af As Float = Result.a / 255
    Result.r = Bit.And(0xff, mBuffer(cp + ri)) / af
    Result.g = Bit.And(0xff, mBuffer(cp + gi)) / af
    Result.b = Bit.And(0xff, mBuffer(cp + bi)) / af
#End If
    
    Return Result
End Sub

setargb( argb(5, 0,0,100))->pm.b = int(100*(5/255)) = 1 (should be 1.96)
getargb()-> b = int(1/(5.0/255)) = 50

setargb( argb(10, 0,0,100))->pm.b = int(100*(10/255)) = 3 (should be 3.92)
getargb()-> b = int(3/(10.0/255)) = 76

Just try to explain why these values are generated if you are interested.
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
Short of modifying BitmapCreator (which I don't want to do), I don't see how we could wiggle in there to avoid premultiplication.

Is there another way to get pixels from a B4XBitmap?
 
Upvote 0
Top