Android Tutorial [B4X] [BitmapCreator] Maximizing Performance with BC

1. This tutorial is about BitmapCreator v3.5+.
2. It is relevant for B4A, B4i and B4J.
3. The features discussed are only needed when making hundreds or more drawings per second.

Drawing Bitmaps

When you call bc.DrawBitmap, a new BC is created, the pixels from the bitmap are copied to the new BC and then it is drawn with DrawBitmapCreator.
It is better to convert the Bitmap to BC yourself and draw it with DrawBitmapCreator. It provides more options and it allows you to reuse the BC without copying the pixels from the bitmap each time.

To Blend Or Not To Blend

When SkipBlending is False, transparent and semi-transparent pixels in the source BC are blended (combined) with the target BC.

SkipBlending = False:

SS-2018-06-07_15.07.37.png


SkipBlending = True:

SS-2018-06-07_15.06.47.png


Without optimizations, that will soon be discussed, drawing with SkipBlending = True can be 20+ times faster. It is much faster because the drawing is done by simply copying the data from one bytes array to another.

One clear case where there is no reason to draw with blending is with solid BCs. The target BC will be overwritten anyway.

Blending Optimizations

As we do need to draw with blending in many cases we can make the drawing much faster with the following optimization:
After the source BC is ready you should call bc.BuildPatternCache. A data structure is created that holds information about the solid, semitransparent and transparent pixels in the BC. Later when this BC is drawn over another BC this information will be used to improve the drawing performance.
You only need to call bc.BuildPatternCache once (unless you later modify the BC).

Code from GameUtils class that converts a bitmap to BC:
B4X:
Public Sub BitmapToBitmapCreator (bmp As B4XBitmap, IgnoreSemiTransparent As Boolean) As BitmapCreator
   Dim bc As BitmapCreator
   bc.Initialize(bmp.Width, bmp.Height)
   bc.CopyPixelsFromBitmap(bmp)
   bc.BuildPatternCache (IgnoreSemiTransparent)
   Return bc
End Sub
As you can see in the above code we immediately call BuildPatternCache and forget about it.

Drawing semi-transparent pixels is the slowest operation. To further improve the performance we can treat semi-transparent pixels as if they are solid or fully transparent (based on the alpha level). This is done by setting the IgnoreSemiTransparent parameter to True.

IgnoreSemiTransparent = True:
SS-2018-06-07_15.23.53.png


IgnoreSemiTransparent = False:
SS-2018-06-07_15.25.26.png


Some numbers, tested with B4J:

Mode .......................... Number of sprites per second
SkipBlending=True .................... 1.1M+
SkipBlending=False .................... 53K
SkipBlending=False + bc.BuildPatternCache(False) 202K
SkipBlending=False + bc.BuildPatternCache(True) 590K

As you can see the improvement is huge.

Asynchronous Drawings

Instead of making the drawings with the main thread, we can build a list with all the drawing tasks and call bc.DrawBitmapCreatorsAsync. The drawings will be made with a background thread.
It looks like this:
B4X:
Wait For (MainBC.DrawBitmapCreatorsAsync(tasks)) Complete (bmp As B4XBitmap)
Each item in the list is a DrawTask type that holds the same parameters that should be passed to DrawBitmapCreator.
As a bonus it also returns the created bitmap.

While the background thread is making the drawings, the main thread is free to do other things like receiving user input and updating the display.
Note that you shouldn't do any other drawings with this BC until the Complete event is raised.

Scale (B4A only):

BitmapCreator creates bitmaps with a scale of 1.0. You should treat all devices as if their scale is 1.0.
To create a BC with the same size as a View:
B4X:
'this code will work correctly in all platforms
bc.Initialize(ImageView1.Width / xui.Scale, ImageView1.Height / xui.Scale)
If you are loading a bitmap then don't use 'dip' units.


Final Result

 
Last edited:
Top