B4A Library [Lib] Accelerated surface

This library provides a dedicated drawing surface embedded inside a view, which benefits from the hardware acceleration. With it, you get the speed of OpenGL for 2D without the complexity.
It includes many Canvas methods (with anti-aliasing, matrix and camera) and a few useful methods for Bitmaps and Drawables (AlterColors, Crop, LoadNinePatch, ReduceColors, SetDensity, etc.). You can import the Matrix, Camera, Paint and Path objects from another library (if they are not wrapped).

surface.png

imageviews.jpg


You can use it to make games:

princess_tiles.png

space_enemies.png


It includes a tool (TextFactory) to do nice titles (that you can export to a bitmap):

textfactory.jpg


It supports Porter-Duff modes, color filters and texture blending (the processing time is very fast):

pdmodes.jpg


The archive includes four benchmarks:

Perf.png


Because of my lack of (free) time, don't expect answers from me about this library if you're not one of my donors.

Download the latest version (1.12)
To convert a project from v0.9x to v1.x, read this.

Hints & Tips

This library does not work with Android versions < 2 (Eclair and Froyo may exhibit performance problems, so I recommend only Gingerbread or a newer version for animations with a high FPS).
The hardware acceleration is not enabled with Android versions < 3.
 

Attachments

  • Java source - AcceleratedSurface.zip
    21.3 KB · Views: 653
Last edited:

canalrun

Well-Known Member
Licensed User
Longtime User
Hello,
I am using the ImageUtilities from the 1.12 AcceleratedSurface library. I need to load two bitmaps that can sometimes be huge – 3000 x 2000 pixels or more.

When the bitmaps are too big I get the "java.lang.OutOfMemoryError". Sometimes the first bitmap will load properly, but the second bitmap will generate the out of memory error. I have the following code hoping that if the load bitmap fails it will generate an exception in which case I will try resizing the bitmap and loading again.

When the LoadScaledBitmap fails with an "out of memory error" it doesn't generate an exception, the app just closes with the "Sorry your app has closed" error message.

B4X:
  Do While (dflg = 0)
    Try
      bmw = imd.Get("Width") * rsz
      bmh = imd.Get("Height") * rsz 
 
      bmSrc = iu.LoadScaledBitmap(File.DirDefaultExternal, fldSrc & "/" & FileSel.src, bmw, bmh, False)
      bmLyr = iu.LoadScaledBitmap(File.DirDefaultExternal, fldSrc & "/" & FileSel.lyr, bmw, bmh, False)
     
      dflg = 1 
    Catch
      rsz = rsz - 0.1
     
      If (rsz < 0.1) Then dflg = -1
    End Try
  Loop

Is there some way to detect these "out of memory" errors so that I can try again requesting a smaller resized bitmap?

Thanks,
Barry.
 

Informatix

Expert
Licensed User
Longtime User
Hello,
I am using the ImageUtilities from the 1.12 AcceleratedSurface library. I need to load two bitmaps that can sometimes be huge – 3000 x 2000 pixels or more.

When the bitmaps are too big I get the "java.lang.OutOfMemoryError". Sometimes the first bitmap will load properly, but the second bitmap will generate the out of memory error. I have the following code hoping that if the load bitmap fails it will generate an exception in which case I will try resizing the bitmap and loading again.

When the LoadScaledBitmap fails with an "out of memory error" it doesn't generate an exception, the app just closes with the "Sorry your app has closed" error message.

B4X:
  Do While (dflg = 0)
    Try
      bmw = imd.Get("Width") * rsz
      bmh = imd.Get("Height") * rsz

      bmSrc = iu.LoadScaledBitmap(File.DirDefaultExternal, fldSrc & "/" & FileSel.src, bmw, bmh, False)
      bmLyr = iu.LoadScaledBitmap(File.DirDefaultExternal, fldSrc & "/" & FileSel.lyr, bmw, bmh, False)
   
      dflg = 1
    Catch
      rsz = rsz - 0.1
   
      If (rsz < 0.1) Then dflg = -1
    End Try
  Loop

Is there some way to detect these "out of memory" errors so that I can try again requesting a smaller resized bitmap?

Thanks,
Barry.
I suppose that the OoM error happens when the image is rescaled, not when it is loaded. That explains why LoadScaledBitmap does not catch it. My advice: check the memory available before rescaling the image (you need (original_width * original_height * 4) + (final_width * final_height * 4) bytes). Or use a large heap.
 
Last edited:

Fusseldieb

Active Member
Licensed User
Longtime User
Sorry for this "nooby" question, but I'm trying to understand this library with these examples which you have attached, but I don't know for what "AcSf_Draw" is.
I want just draw a simple image on the screen and animate it with a timer. How can this be realised?

Thanks in advance :)
 

Informatix

Expert
Licensed User
Longtime User
Sorry for this "nooby" question, but I'm trying to understand this library with these examples which you have attached, but I don't know for what "AcSf_Draw" is.
I want just draw a simple image on the screen and animate it with a timer. How can this be realised?

Thanks in advance :)
You don't need a timer. You have to call AcSf.StartRegularUpdateAndDraw(16) in your Resume event and AcSf.StopRegularDraw in your Pause event (to start and stop the timer used internally which is far more reliable than the B4A timer), then put the drawing code in the AcSf_Draw event and the update of your game data in the AcSf_Update event. That's all. To draw your image, the simplest function to use is DrawBitmapAt.
 

Fusseldieb

Active Member
Licensed User
Longtime User
You have to call AcSf.StartRegularUpdateAndDraw(16)
Thanks so much for the reply. But if I have more than one item to move? Normally i make it with varios timers. Here I have only one??
Can you please write me a simple code to show how can I make that an image goes Left=Left+1 all 100ms or so?...
This would help me a lot understanding this...

Thanks Informatix
 

Informatix

Expert
Licensed User
Longtime User
Thanks so much for the reply. But if I have more than one item to move? Normally i make it with varios timers. Here I have only one??
Can you please write me a simple code to show how can I make that an image goes Left=Left+1 all 100ms or so?...
This would help me a lot understanding this...

Thanks Informatix
One timer per item! You should absolutely avoid it. Try that with 100 sprites on screen and you'll see by yourself the result.
And you should avoid also Left=Left+1. Please read the tutorial about games that I wrote (especially the "Mind the step" chapter). The AcSf_Update event or the ElapsedTimeSinceLastDraw function will give you the elasped time.
The library is provided with sixteen examples. You should look at their code too.
 

sorex

Expert
Licensed User
Longtime User
@Informatix : is there a way to update the canvas from outside the _draw sub ?

I tried the code below but it's not drawing a line.

B4X:
AccSurface.Invalidate
AccCvs.DrawLine(0,0,100,100,Colors.White,1,False)   
AccSurface.Invalidate       
End Sub

Sub AccSurface_Draw(AC As AS_Canvas)
AccCvs=AC
End Sub
 

thedesolatesoul

Expert
Licensed User
Longtime User
@Informatix : is there a way to update the canvas from outside the _draw sub ?

I tried the code below but it's not drawing a line.

B4X:
AccSurface.Invalidate
AccCvs.DrawLine(0,0,100,100,Colors.White,1,False)  
AccSurface.Invalidate      
End Sub

Sub AccSurface_Draw(AC As AS_Canvas)
AccCvs=AC
End Sub
You shouldnt do that. Do it inside the Draw sub, thats the whole point of the draw sub.
 

sorex

Expert
Licensed User
Longtime User
Is there a way to know to which view the canvas in the _draw event belongs to?

I have several small Accelerated Views on the activity.
 

thedesolatesoul

Expert
Licensed User
Longtime User
Is there a way to know to which view the canvas in the _draw event belongs to?

I have several small Accelerated Views on the activity.
But each AcceleratedSurface on the activity will have its own event? Do you have an array of AS, all calling the same event?

EDIT: Also did you try to check the 'sender' and sender tag?
 

sorex

Expert
Licensed User
Longtime User
Actually, I use the same draw event for all of them since the amount is not fixed and the drawing code is the same for all of them.

The sender returns the timer that initiates the invalidate.
 

Informatix

Expert
Licensed User
Longtime User
Actually, I use the same draw event for all of them since the amount is not fixed and the drawing code is the same for all of them.

The sender returns the timer that initiates the invalidate.
You should use a single Accelerated Surface. Multiplying the internal timers to update regularly the canvas is a very bad idea. You can draw a lot of different things in the same AcSf canvas.
 

sorex

Expert
Licensed User
Longtime User
Well, it are panel based buttons where each had a Acc.Surf. for audio representation.

I went for plain canvas at the moment which works fine, not sure if Acc.Surf. would up a lot on line drawing aswell?
 

Informatix

Expert
Licensed User
Longtime User
Well, it are panel based buttons where each had a Acc.Surf. for audio representation.

OK. If you draw only once (no timer is used), there's no problem. If not, then you should manage yourself a single timer and call all Draw events for each tick.

I don't know a way to identify which AcSf raised the event. "Sender" should be Null most of the time.

I went for plain canvas at the moment which works fine, not sure if Acc.Surf. would up a lot on line drawing aswell?
Sorry but I'm not sure of the meaning of the last part of your sentence.
 
Top