Android Question BitmapCreator and pixel color

yo3ggx

Active Member
Licensed User
Longtime User
Hello,

I have the following piece of code:

pixel color:
    Dim bmc As BitmapCreator
    Dim i As Int
    Dim linePixels(128) As Int
    Dim bc As ByteConverter

    bmc.Initialize(128,1)

    For i = 0 To 127
        linePixels(i) = Colors.Red
    Next
    bc.LittleEndian = True
    Bit.ArrayCopy(bc.IntsToBytes(linePixels),0,bmc.buffer,0,4*128)

    XUIViewsUtils.SetBitmapAndFill(imgTest,bmc.Bitmap)

Then I display the image on a panel.
With Colors.Red, the image is Blue
With Colors.Green, the image is Green
With Colors.Blue, the image is Red

looks like Red and Blue corresponding bytes are reversed. This is a normal behavior?
 
Last edited:

yo3ggx

Active Member
Licensed User
Longtime User
The code is much more complex, I just provided a short example to reproduce the behavior.
I have to build the bitmap line by line (1024 pixels per line). SetColor per pixel is to slow, as the line of pixels is available as input data. Anyway, I suppose the behavior will be the same, as in my code I pass the color as an int too.

Looks like color representation as Colors.Red does not provide the expected color integer value for BitmapCreator.

Tried to change linePixels like that.

B4X:
    For i = 0 To 127
        k = i*255/127
        linePixels(i) = k
    Next
With LittleEndian = True, I get a gradient from Blue to Magenta from left to right. How can both red and blue can appear using an int from 0 to 255?
With LittleEndian = False, I get a gradient from Blue to Black from left to right. What is expected, right from left instead of left to right.

Very strange ...
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
I seem to remember experiencing this in the early days of Android and B4A many years ago when doing low level bitmaps and expecting ARGB colours. Unfortunately I can't remember what the project was, nor the solution though it was probably just swap them as it seemed to work.

The android Bitmap format enumeration for full 8bit colour depth is
ANDROID_BITMAP_FORMAT_RGBA_8888
So it looks like maybe its little endian ABGR as you have found but maybe someone with better knowledge of Android graphics than I can shed more light.

I've no idea about the gradient without a project to play with to see it.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
To convert an int from Colors.X to something that can be used for BitmapCreator I'm doing (as I don't need transparency):

B4X:
Dim newcolor as int
newcolor = oldcolor*256+255            ' Bit.Shift can be used too
bc.LittleEndian = False

And the result is the expected one.
In B4J this conversion is not necessary, only in B4A, but you must use:
B4X:
bc.LittleEndian = True

Really strange ..
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
In B4J is straight forward, but in B4A I have to do this manual conversion.

What other (built-in, more efficient) option I have to pass a line of pixels (as an Int array) to BitmapCreator?
 
Upvote 0

teddybear

Active Member
Licensed User
In B4J is straight forward, but in B4A I have to do this manual conversion.

What other (built-in, more efficient) option I have to pass a line of pixels (as an Int array) to BitmapCreator?

You can do bmc.setcolor(x, y, colors.red)
B4X:
For i = 0 To 127
        bmc.setcolor(i,0,colors.red)
Next
XUIViewsUtils.SetBitmapAndFill(imgTest,bmc.Bitmap)
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
into your one-pixel-wide bmc.Initialize(128,1) end up near to what you're aiming for?
It was just as a simple example how color as an int is handled by BitmapCreator in B4A (different than in B4J).

In fact I have a 1024xYYY bitmap, where YYY can be any value between 100 and 1000.
The line of pixels is always 1024 in length, as input data (Ints array).

To be more specific, to behave the same:
In B4J: Color as Int is represented AARRGGBB, LittleEndian = True when converting Ints array to byte array
In B4A: Color as Int is represented RRGGBBAA, LittleEndian = False when converting Ints array to byte array
 
Upvote 0

emexes

Expert
Licensed User
pixel color:
Dim linePixels(128) As Int
...
Bit.ArrayCopy(bc.IntsToBytes(linePixels),0,bmc.buffer,0,128)

I don't have a working B4A setup here, but I've been mucking about in B4J with your sample code, and eventually I noticed that you have an array of 128 ints which is converted into an array of 512 bytes, but ArrayCopy is only copying the first 128 bytes. I added a multiply-by-four to the line:

pixel color:
Bit.ArrayCopy(bc.IntsToBytes(linePixels), 0, bmc.buffer, 0, 128 * 4)

and things are making way more sense here now. 🍻
 
Upvote 0

emexes

Expert
Licensed User
and things are making way more sense here now. 🍻

Next, instead of shifts & ors or multiplies & adds, to combine the component Byte values into a single Int, I've been using:

edit: multiplies are especially bug prone, thanks to Ints being signed, and thus the high bit acting somewhat rogue :oops:

edit: ugh, that reminds me: Bytes are signed too, and the high bit is no fun with them either

B4X:
For i = 0 To 127
    'B4J under Windows: byte order is (B, G, R, A)
    linePixels(i) = bc.IntsFromBytes(Array As Byte(0, 0, i, 255))(0)
Next

which I feel is a marginally :rolleyes: simpler and clearer way of seeing what the code is trying to do.

Here (B4J under Windows) this creates a gradient that is black on the left to half-bright red on the right. What does it do under Android?
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I don't have a working B4A setup here, but I've been mucking about in B4J with your sample code, and eventually I noticed that you have an array of 128 ints which is converted into an array of 512 bytes, but ArrayCopy is only copying the first 128 bytes. I added a multiply-by-four to the line:
...
Is a typo in my example, as is manually reduced from what I have in my app.
Of course is 4x128. This not the issue. The issue is with the color int format required by BitmapCreator in B4A.
As written in my message, behavior in B4J is the expected one, so no issue at all in B4J. Only in B4A the format is different.
 
Upvote 0

teddybear

Active Member
Licensed User
Is a typo in my example, as is manually reduced from what I have in my app.
Of course is 4x128. This not the issue. The issue is with the color int format required by BitmapCreator in B4A.
As written in my message, behavior in B4J is the expected one, so no issue at all in B4J. Only in B4A the format is different.
The pixel underlying format is different on different OS, in b4a a pixel byte order is RGBA, but b4j is BGRA ..., so you need convert color int to bytes according to different OS, as Erel said you'd better use built-in method to manipulate them as possible.
 
Upvote 0

emexes

Expert
Licensed User
Reinstalled B4A, waited for development phone to download Android updates, tried this:

B4X:
    For i = 0 To 127
#if b4a
        'B4A under android: byte order is (R, G, B, A)
        LinePixels(i) = bc.IntsFromBytes(Array As Byte(i, 0, 0, 255))(0)
#else
        'B4J under windows: byte order is (B, G, R, A)
        LinePixels(i) = bc.IntsFromBytes(Array As Byte(0, 0, i, 255))(0)
#end if
    Next

with both B4J and B4A, seems to be working. But I think the conditional IntsFromBytes ordering is best put into a separate Sub rather than repeated throughout your code, which raises the question of... are we slowing sinking back to your original issue of:

SetColor per pixel is to slow
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
Your code is just a solution for the minimal example provided by me in the first post (one color).
LinePixels is in my case an ints array (1024 in length) in the B4J format and is an input data, so I have to convert each pixel color independently.

On the other side, I didn't tried to see if SetPixel accept the right color format (as in B4J), not as when writing directly in BitmapCreator. If yes, Instead of calculating each pixel to build a new array and then copy the aray, I can use SetPixel directly in BitCreator. May be even faster.
 
Upvote 0

emexes

Expert
Licensed User
May be even faster.

I'm in the midst of timing it. Using SetARGB rather than SetColor. Got unbelievable times, then remembered that profiling in debug mode will get me a whack on the knuckles from you-know-who :oops: so now I'm trying to work out where #BridgeLogger belongs. 🍻
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I forgot that in my app I build the linePixels using a palette array (byte to color). As I'm building the palette using the rule for B4A, SetPixels is no more required, nor efficient.
I can use directly ArrayCopy as it is now.

So the difference between B4A and B4J is just the way the palette array is built. And this is done one time only.
 
Upvote 0

emexes

Expert
Licensed User
On the other side, I didn't tried to see if SetPixel accept the right color format (as in B4J), not as when writing directly in BitmapCreator. If yes ... I can use SetPixel directly in BitCreator. May be even faster.

Yeah, it'd be pretty disappointing if ARGBColor color channels weren't automatically configured to match the display. 🤔

B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
   
    Root = Root1
    Root.LoadLayout("MainPage")
   
    Dim bmc As BitmapCreator
    Dim i As Int
    Dim LinePixels(128) As Int
    Dim bc As ByteConverter

    bmc.Initialize(128, 1)
   
    Dim TempARGB As ARGBColor
    TempARGB.Initialize    'sets all to 0, including alpha (wth?)
    TempARGB.a = 255

Log("Starting timing test")
Dim StartTime As Long = DateTime.Now
For ProfilingDoLots = 1 To 100000
   
    For i = 0 To 127
        TempARGB.r = i * 2
        bmc.SetARGB(i, 0, TempARGB)
    Next
   
Next
Dim EndTime As Long = DateTime.Now
Log("Finished timing test")
Log(((EndTime - StartTime) / 1000))
   
    '''no longer needed - is already in bmc
    '''Bit.ArrayCopy(bc.IntsToBytes(LinePixels), 0, bmc.buffer, 0, LinePixels.Length * 4)

    XUIViewsUtils.SetBitmapAndFill(ImageView1, bmc.Bitmap)
   
End Sub

in Release Mode results in - for 100,000 x 128 pixel sets, and on a cheap-end phone too (I think AUD $70, definitely under $100) :

1663155719515.png
 
Last edited:
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
BitmapCreator does paletted stuff too? I'm wondering if it converts from your palette table index bytes to internal ARGB format.
Not as I'm aware. I build the pallete as a Ints Array with 256 values. In fact initially the palette is a bitmap from where I extract the colors using GetPixel. The colors returned by GetPixel are the expected ones. I suppose SetPixel in BitmapCreator do the same, but as my problem is currently solved in an acceptable way, I will not further dig on this.
 
Upvote 0
Top