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: 784
Last edited:

johnaaronrose

Active Member
Licensed User
Longtime User
No. You have to place the AS in a ScrollView2D panel.



Yes. And it's supposed to work without trouble out of the box.

Ok, I now have a Panel (occupying part of the screen). I have an sv2d & an acsf occupying the same area as the Panel. I have the sv2d.Panel with its left & top the same as the sv2d but the width & height being the same as a large photo which is displayed in the sv2d by means of SetBackgroundImage for the sv2d.Panel. At runtime, I see the photo in the area. I am not able to scroll it: I think that the reason is that I have a Touch event for the Panel which prevents that. Is that correct?

The reason for the Touch event for the Panel is to store the screen coordinates where touched and to use these to draw a point (actually an X with a small circle round it) by means of the appropriate AC.Draw (where AC is the acsf's Canvas) commands in the acsf_Draw sub. Should I be using a Panel with a Touch event to obtain the screen coordinates? A Click event for the Panel seems better but it doesn't supply the screen coordinates.
 

Informatix

Expert
Licensed User
Longtime User
Ok, I now have a Panel (occupying part of the screen). I have an sv2d & an acsf occupying the same area as the Panel. I have the sv2d.Panel with its left & top the same as the sv2d but the width & height being the same as a large photo which is displayed in the sv2d by means of SetBackgroundImage for the sv2d.Panel. At runtime, I see the photo in the area. I am not able to scroll it: I think that the reason is that I have a Touch event for the Panel which prevents that. Is that correct?

The reason for the Touch event for the Panel is to store the screen coordinates where touched and to use these to draw a point (actually an X with a small circle round it) by means of the appropriate AC.Draw (where AC is the acsf's Canvas) commands in the acsf_Draw sub. Should I be using a Panel with a Touch event to obtain the screen coordinates? A Click event for the Panel seems better but it doesn't supply the screen coordinates.
Do simple things:
- place an AcSf in the ScrollView2D inner panel (left = 0, top = 0)
- resize the panel and the AcSf to the dimensions of the picture
- set the background of the AcSf to your picture
- handle the touch events of the AcSf
Nothing more.
 

johnaaronrose

Active Member
Licensed User
Longtime User
Do simple things:
- place an AcSf in the ScrollView2D inner panel (left = 0, top = 0)
- resize the panel and the AcSf to the dimensions of the picture
- set the background of the AcSf to your picture
- handle the touch events of the AcSf
Nothing more.
I've done as you suggested. The Acsf Touch event's sub is repeatedly executed (with Action=1 & 2) and the Acsf Draw event's sub is occasionally called, but the ScrollView2D ScrollChange event's sub is never executed: proved by use of Log statements. Also, scrolling does not take place (nor do the scrolling lines/bars ever appear.
PS there is no Panel Touch event sub now as it's now become the Acsf Touch event sub.

Some coding snippets:

B4X:
Process_Globals
Dim MapBitmap As Bitmap
Dim GPSDir As String
Dim MapFilename As String

B4X:
Sub Globals
Dim sv2dMap As ScrollView2D
Dim acsfMap As AcceleratedSurface
Dim MapHorizontalScrollPosition As Int
Dim MapVerticalScrollPosition As Int

B4X:
Sub Activity_Create (FirstTime As Boolean)
sv2dMap.Initialize(0, 0, "sv2dMap")
Activity.AddView(sv2dMap, pnlMap.Left, pnlMap.Top, pnlMap.Width, pnlMap.Height)
MapBitmap.Initialize(GPSDir, MapFilename)
sv2dMap.Panel.Width = MapBitmap.Width
sv2dMap.Panel.Height = MapBitmap.Height
' Centre map
MapHorizontalScrollPosition = Round2((sv2dMap.Width - sv2dMap.Panel.Width)/2, 0)
MapVerticalScrollPosition = Round2((sv2dMap.Height - sv2dMap.Panel.Height)/2, 0)
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
Do While sv2dMap.ScrollingIsFinished = False
  DoEvents
Loop
acsfMap.Initialize("acsfMap", True)
Activity.AddView(acsfMap, 0, 0, sv2dMap.Panel.Width, sv2dMap.Panel.Height)
acsfMap.SetBackgroundImage(MapBitmap)
DoEvents

B4X:
Sub Activity_Pause
MapHorizontalScrollPosition = sv2dMap.HorizontalScrollPosition
MapVerticalScrollPosition = sv2dMap.VerticalScrollPosition

B4X:
Sub sv2dMap_ScrollChanged (PosX As Int, PosY As Int)
MapHorizontalScrollPosition = PosX
MapVerticalScrollPosition = PosY
End Sub

B4X:
Sub acsfMap_Touch (Action As Int, X As Float, Y As Float, Event As Object)
If Action = Activity.ACTION_UP Then
  MapPoint.ScreenX = X
  MapPoint.ScreenY = Y
  acsfMap.Invalidate  'causes call to acsfmap_Draw
End If
End Sub

B4X:
Sub acsfMap_Draw (AC As AS_Canvas)
Dim xc, yc, x1, y1, x2, y2, x3, y3 As Int
xc = MapPoint.ScreenX
yc = MapPoint.ScreenY
x1 = xc - GPSCross
y1 = yc - GPSCross
x2 = xc + GPSCross
y2 = yc + GPSCross
x3 = MapPointOld.ScreenX
y3 = MapPointOld.ScreenY
Dim rectMapPosition As Rect
rectMapPosition.Initialize(x1, y1, x2 + 1, y2 + 1)
AC.DrawRect(rectMapPosition, Colors.Red, False, 2dip, True)
AC.DrawLine(x1, yc, x2, yc, Colors.LightGray, 1dip, True)
AC.DrawLine(xc, y1, xc, y2, Colors.LightGray, 1dip, True)
AC.DrawCircle(xc, yc, GPSRadius, Colors.DarkGray, False, 3dip, True)
AC.DrawPoint(xc, yc, Colors.Red)
 

ernschd

Active Member
Licensed User
Longtime User
2) your code draws the last move then clears the drawing (do you really need StartRegularDraw?)... With the AS library, you have to store all points of the path then draw the lines connecting these points in the Draw event.
I thought i have to use StartRegularDraw for the drawing of the path?

So here's my code - but the line is no continuous path:
B4X:
Sub Globals
   Dim Panel1 As Panel
   Dim AcSf As AcceleratedSurface
   Dim mX As Float, mY As Float
   Dim px, py As Int   
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Panel1.Initialize("")
   Panel1.Color = Colors.White
   Activity.AddView(Panel1, 0, 0, 100%x, 100%y)
   AcSf.Initialize("AcSf", True)   
   Activity.AddView(AcSf, Panel1.Left, Panel1.Top, Panel1.Width, Panel1.Height)   
End Sub

Sub Activity_Resume
   AcSf.StartRegularDraw(100)
End Sub

Sub AcSf_Touch(Action As Int, X As Int, Y As Int, Event As Object)
   If Action = 0 Then 'mouse down constant
     px = X
     py = Y
   Else
     mX = X
     mY = Y       
   End If   
End Sub

Sub AcSf_Draw(AC As AS_Canvas)
   Dim P As AS_Path
   P.Initialize(px, py)
   P.LineTo(mX, mY)
   AC.DrawPath(P, Colors.Black, False, 3, True)   
End Sub
 

Informatix

Expert
Licensed User
Longtime User
I've done as you suggested. The Acsf Touch event's sub is repeatedly executed (with Action=1 & 2) and the Acsf Draw event's sub is occasionally called, but the ScrollView2D ScrollChange event's sub is never executed: proved by use of Log statements. Also, scrolling does not take place (nor do the scrolling lines/bars ever appear.
PS there is no Panel Touch event sub now as it's now become the Acsf Touch event sub.

Some coding snippets:

B4X:
Process_Globals
Dim MapBitmap As Bitmap
Dim GPSDir As String
Dim MapFilename As String

B4X:
Sub Globals
Dim sv2dMap As ScrollView2D
Dim acsfMap As AcceleratedSurface
Dim MapHorizontalScrollPosition As Int
Dim MapVerticalScrollPosition As Int

B4X:
Sub Activity_Create (FirstTime As Boolean)
sv2dMap.Initialize(0, 0, "sv2dMap")
Activity.AddView(sv2dMap, pnlMap.Left, pnlMap.Top, pnlMap.Width, pnlMap.Height)
MapBitmap.Initialize(GPSDir, MapFilename)
sv2dMap.Panel.Width = MapBitmap.Width
sv2dMap.Panel.Height = MapBitmap.Height
' Centre map
MapHorizontalScrollPosition = Round2((sv2dMap.Width - sv2dMap.Panel.Width)/2, 0)
MapVerticalScrollPosition = Round2((sv2dMap.Height - sv2dMap.Panel.Height)/2, 0)
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
Do While sv2dMap.ScrollingIsFinished = False
  DoEvents
Loop
acsfMap.Initialize("acsfMap", True)
Activity.AddView(acsfMap, 0, 0, sv2dMap.Panel.Width, sv2dMap.Panel.Height)
acsfMap.SetBackgroundImage(MapBitmap)
DoEvents

B4X:
Sub Activity_Pause
MapHorizontalScrollPosition = sv2dMap.HorizontalScrollPosition
MapVerticalScrollPosition = sv2dMap.VerticalScrollPosition

B4X:
Sub sv2dMap_ScrollChanged (PosX As Int, PosY As Int)
MapHorizontalScrollPosition = PosX
MapVerticalScrollPosition = PosY
End Sub

B4X:
Sub acsfMap_Touch (Action As Int, X As Float, Y As Float, Event As Object)
If Action = Activity.ACTION_UP Then
  MapPoint.ScreenX = X
  MapPoint.ScreenY = Y
  acsfMap.Invalidate  'causes call to acsfmap_Draw
End If
End Sub

B4X:
Sub acsfMap_Draw (AC As AS_Canvas)
Dim xc, yc, x1, y1, x2, y2, x3, y3 As Int
xc = MapPoint.ScreenX
yc = MapPoint.ScreenY
x1 = xc - GPSCross
y1 = yc - GPSCross
x2 = xc + GPSCross
y2 = yc + GPSCross
x3 = MapPointOld.ScreenX
y3 = MapPointOld.ScreenY
Dim rectMapPosition As Rect
rectMapPosition.Initialize(x1, y1, x2 + 1, y2 + 1)
AC.DrawRect(rectMapPosition, Colors.Red, False, 2dip, True)
AC.DrawLine(x1, yc, x2, yc, Colors.LightGray, 1dip, True)
AC.DrawLine(xc, y1, xc, y2, Colors.LightGray, 1dip, True)
AC.DrawCircle(xc, yc, GPSRadius, Colors.DarkGray, False, 3dip, True)
AC.DrawPoint(xc, yc, Colors.Red)

It's the second time we have a discussion on this subject. Please re-read my former posts. One of them includes a very basic example. There is absolutely no reason for the AcSf to block the interception of events by the SV2D.
In your code, you do not do what I explained in post #102. Please read what I write or it will become quickly difficult to help you.
 

Informatix

Expert
Licensed User
Longtime User
I thought i have to use StartRegularDraw for the drawing of the path?

So here's my code - but the line is no continuous path:
B4X:
Sub Globals
   Dim Panel1 As Panel
   Dim AcSf As AcceleratedSurface
   Dim mX As Float, mY As Float
   Dim px, py As Int  
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Panel1.Initialize("")
   Panel1.Color = Colors.White
   Activity.AddView(Panel1, 0, 0, 100%x, 100%y)
   AcSf.Initialize("AcSf", True)  
   Activity.AddView(AcSf, Panel1.Left, Panel1.Top, Panel1.Width, Panel1.Height)  
End Sub

Sub Activity_Resume
   AcSf.StartRegularDraw(100)
End Sub

Sub AcSf_Touch(Action As Int, X As Int, Y As Int, Event As Object)
   If Action = 0 Then 'mouse down constant
     px = X
     py = Y
   Else
     mX = X
     mY = Y      
   End If  
End Sub

Sub AcSf_Draw(AC As AS_Canvas)
   Dim P As AS_Path
   P.Initialize(px, py)
   P.LineTo(mX, mY)
   AC.DrawPath(P, Colors.Black, False, 3, True)  
End Sub

Ok, you fixed the two problems, but as the canvas is cleared each time you enter the Draw event handler, you have to store the data if you want to draw a path with more than one line (move "Dim P as As_Path" to Globals and move "P.Initialize(px, py)" under py=Y).
A better solution than calling StartRegularDraw is to draw the lines only when your finger moves, so add "AcSf.Invalidate" under my=Y and remove StartRegularDraw.
 

johnaaronrose

Active Member
Licensed User
Longtime User
It's the second time we have a discussion on this subject. Please re-read my former posts. One of them includes a very basic example. There is absolutely no reason for the AcSf to block the interception of events by the SV2D.
In your code, you do not do what I explained in post #102. Please read what I write or it will become quickly difficult to help you.
Thanks for that: it now works. I'd left this app for a few months partly because I was not able to work out (from your example) what I'd done wrong. I have just come back to my app & I saw my mistakes. I should have had 'sv2dMap.Panel.AddView(acsfMap, 0, 0, -1, -1)' instead of
'Activity.AddView(acsfMap, 0, 0, sv2dMap.Panel.Width, sv2dMap.Panel.Height)' and should have had 'acsfMap.Enabled = False' in Activity_Resume.

I've just noticed that there are lots of unexpected calls to acsfMap_Draw. the only acfsMap.Invalidate command that I have is in the pnlMap.Touch sub (pnlMap is the view on which sv2dMap is 'located') which is only executed if Action=Activity.ACTION_UP: in fact it is never executed when I run the app. Is there any other way that acsfMap.Draw sub can be executed (i.e. without acsfmap.Invalidate etc)?
 
Last edited:

uhzeg

Member
Licensed User
Longtime User
Hi Informatix,
thanks for the great lib !

i would like to make use of one of your latest changes: to dim an AS_Canvas and use it in any Sub.
I use
B4X:
Dim AC_2draw As AS_Canvas
in
B4X:
Sub Globals
and would like to call it like
B4X:
AC_2draw.DrawLine(
in a Sub. However, I get a NullPointerException (at flm.b4a.accelerview.AcceleratedCanvas.ResetWithColor(AcceleratedCanvas.java:257)).

Do I miss a
B4X:
Initialize()
, or how can I use the new dim function for it?

Thanks a lot
uhzeg
 

Informatix

Expert
Licensed User
Longtime User
Hi Informatix,
thanks for the great lib !

i would like to make use of one of your latest changes: to dim an AS_Canvas and use it in any Sub.
I use
B4X:
Dim AC_2draw As AS_Canvas
in
B4X:
Sub Globals
and would like to call it like
B4X:
AC_2draw.DrawLine(
in a Sub. However, I get a NullPointerException (at flm.b4a.accelerview.AcceleratedCanvas.ResetWithColor(AcceleratedCanvas.java:257)).

Do I miss a
B4X:
Initialize()
, or how can I use the new dim function for it?

Declaring just "Dim AC As AS_Canvas" does nothing. The class is instantiated but its internal canvas reference is Null. You have to set this object with the value provided by the Draw event. You can imagine for example to raise the Draw event only once (it is raised at least once when you create an AcSf) to set the value of a global object of type AS_Canvas, then you can use this object anywhere.
 

sorex

Expert
Licensed User
Longtime User
I'm having a look at your lib again.

the water example how smooth is that supposed to run?

it's static at 59fps but it doesn't look as smooth like for example that kula jump (or something) game
while less lookups/checks are needed for this.
 

Informatix

Expert
Licensed User
Longtime User
I'm having a look at your lib again.

the water example how smooth is that supposed to run?

it's static at 59fps but it doesn't look as smooth like for example that kula jump (or something) game
while less lookups/checks are needed for this.
It runs probably smoothly on fast devices and a bit choppy on a lot of others. It shows the limits of this library and also how to scroll a texture (this trick is commonly used in games).
 

sorex

Expert
Licensed User
Longtime User
it's a Ployer Momo 11 tablet, far from slow.

the android robot example with 100 sprites ran smooth tho.

when it goes accelareted does that mean it's doing it the Open GL way (like libgdx does) ?
 

sorex

Expert
Licensed User
Longtime User
ok, it's just that libgdx looks (and probably is) a lot more complicated compared to this.
 

Pavka

Member
Licensed User
Longtime User
Hi informatrix,
your lib is realy grate!
Can you tell me please, is it possible to use it making live wallpaper? If yes, how to modify
B4X:
Activity.AddView(AcSf, 0, 0, 100%x, 100%y)
in WallpaperService?

regards
Pavka
 

socialnetis

Active Member
Licensed User
Longtime User
Hi, I'm trying to make a circular crop in a bitmap by code. After searching the B4A forum and watching some examples in stackoverflow I came that some method of this library would help.

Right now I manage to create a circular crop with this code:

B4X:
Sub Activity_Create(FirstTime As Boolean)

    AcSf.Initialize("AcSf",True)
    Activity.AddView(AcSf,50dip,50dip,100dip,100dip)
    SomeBitmap = LoadBitmap(...)                           'SomeBitmap is a 100x100 px bitmap
End Sub

Sub AcSf_Draw(AC As AS_Canvas)
    Dim O As AS_Object
    O = O.CreateCircle(50,True).SetColor(Colors.ARGB(255,0,0,0))
    AC.DrawObjectAt(O,50,50)
    O = O.CreateBitmap(SomeBitmap,True).SetXferMode(O.MODE_SRC_IN)
    AC.DrawObjectAt(O,0,0)
End Sub

This piece of code does exactly what I want, but I have one question:
1) The AcSf is 100dip x 100dip, and the CreateCircle have a radius of 50 (not 50dip), and the same goes to the DrawObjectAt which draws the circle
with the center in (50,50) and not (50dip,50dip). The result is the one that I want, and I tested in a phone and a tablet. And if I use "dip" units, everything is not proporcional as it should be. My question is if this values have nothing to do with dips, and everything would look nice in every resolution/density phone?
 

Informatix

Expert
Licensed User
Longtime User
Hi, I'm trying to make a circular crop in a bitmap by code. After searching the B4A forum and watching some examples in stackoverflow I came that some method of this library would help.

Right now I manage to create a circular crop with this code:

B4X:
Sub Activity_Create(FirstTime As Boolean)

    AcSf.Initialize("AcSf",True)
    Activity.AddView(AcSf,50dip,50dip,100dip,100dip)
    SomeBitmap = LoadBitmap(...)                           'SomeBitmap is a 100x100 px bitmap
End Sub

Sub AcSf_Draw(AC As AS_Canvas)
    Dim O As AS_Object
    O = O.CreateCircle(50,True).SetColor(Colors.ARGB(255,0,0,0))
    AC.DrawObjectAt(O,50,50)
    O = O.CreateBitmap(SomeBitmap,True).SetXferMode(O.MODE_SRC_IN)
    AC.DrawObjectAt(O,0,0)
End Sub

This piece of code does exactly what I want, but I have one question:
1) The AcSf is 100dip x 100dip, and the CreateCircle have a radius of 50 (not 50dip), and the same goes to the DrawObjectAt which draws the circle
with the center in (50,50) and not (50dip,50dip). The result is the one that I want, and I tested in a phone and a tablet. And if I use "dip" units, everything is not proporcional as it should be. My question is if this values have nothing to do with dips, and everything would look nice in every resolution/density phone?
One of my examples (ImageViews.b4a) shows a close but simpler way to do a circular cropping.
To avoid problems with densities, replace all your LoadBitmap and LoadBitmapSample by LoadScaledBitmap (from this library).
 

Douglas Farias

Expert
Licensed User
Longtime User
informatix can you help me to make a game pad on your game exemple?
princestale?
i realy like this game i want to make many changes, but i dont know to get a simple movimente

press button1 = hero go to right
press button2 = hero go to left
etc

can you make a simple pls?
 
Top