Android Code Snippet [BAX] [XUI] BitmapCreator - DrawLine, DrawPath , DrawCircle, DrawArc, DrawImageTrapezoid

BitmapCreator lacks some important instructions on Canvas.
Sometimes switching from BitmapCreator to Canvas and vice versa would slow down the code too much, so it would be better to implement them with BitmapCreator.

I started writing some divers, if someone finds a way to improve it or add other features put other posts.

P.S. Please do not fill the thread with questions and requests for explanations. Open a thread in the questions section

 

Star-Dust

Expert
Licensed User
Longtime User
DRAW LINE
B4X:
Private Sub Drawline(BC As BitmapCreator, x1 As Int, y1 As Int, x2 As Int, y2 As Int, Color As Int)
    Dim Dx,Dy,Diff As Int
    Dim x,y As Float
 
    Dx = x2 - x1
    Dy = y2 - y1
    Diff=Max(Abs(Dx),Abs(Dy))
    For D=0 To Diff
        x= x1 + Dx*D/Diff
        y = y1 + Dy*D/Diff
        If (x>=0 And x<BC.mWidth) And (y>=0 And y<BC.mHeight) Then BC.SetColor(x,y,Color)
    Next
End Sub



DRAW PATH (Empty)
B4X:
Type Point_Type(X as int, Y as int)

    Private Sub DrawPathEmpty(BC As BitmapCreator, VerticesList As List, Color As Int)
    Dim X,Y As Int
 
    Dim P As Point_Type = VerticesList.Get(PointList.Size-1)
    X=P.X
    Y=P.Y
 
    For Each P As Point_Type In VerticesList
        Drawline(BC,X,Y,P.X,P.Y, Color)
        X=P.X
        Y=P.Y
    Next
    End Sub

This sub traces the lines defined by the vertices with the path system. He needs the Sub DrawLine present in this same post
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
DRAW PATH (FILL)
Algorithm ScanLine

Update 0.01
B4X:
Public Sub IsLimit(BC As BitmapCreator,x As Int, y As Int) As Boolean
    Dim cp As Int = x * 4 + y * BC.mWidth * 4
    Return BC.Buffer(cp + 3) = 01
End Sub

Private Sub DrawPathFill(BC As BitmapCreator,PointList As List, Color As Int)
    Dim aColor As ARGBColor = ColorToARGB(Color)
    Dim P As Point_Type = PointList.Get(PointList.Size-1)
    Dim Xl As Int = P.X
    Dim Yl As Int = P.Y
    Dim MaxX As Int = Xl
    Dim MinX As Int = Xl
    Dim MaxY As Int = Yl
    Dim MinY As Int = Yl

    Dim Alimit As ARGBColor = ColorToARGB(xui.Color_ARGB(1,0,0,0))
    For Each P As Point_Type In PointList
            Dim Dx,Dy,Diff As Int
            Dim x,y As Float

            Dx = P.x - Xl
            Dy = P.Y - Yl
            Diff=Max(Abs(Dx),Abs(Dy))
            x = Xl
            y = Yl
            Dim tx  As Double = Dx / Diff
            Dim ty  As Double = Dy / Diff
            For D=0 To Diff
                BC.SetARGB(x, y, Alimit)
                X = Min(Max(x + tx,0),BC.mWidth-1)
                Y = Min(Max(Y + ty,0),BC.mHeight-1)
            Next
        ID=ID+1
        Xl=P.X
        Yl=P.Y
    Next
  
    For Each P As Point_Type In PointList
        AddTouch(ID,ID,P.X,P.Y)
        MinX=Min(MinX,P.X)
        MaxX=Max(MaxX,P.X)
        MinY=Min(MinY,P.y)
        MaxY=Max(MaxY,P.y)
    Next
  
    For y=MinY To MaxY
        Dim StartX As Int = BC.mWidth
        Dim StopX As Int = -1
        For x=MinX To MaxX
            If IsLimit(BC,x,y) Then
                StartX=Min(StartX,x)
                StopX=Max(StopX,x)
            End If
        Next
      
        Dim B As Boolean = False
        For x=StartX To StopX
            If IsLimit(BC,x,y) Then
                If  Not(b) Then
                    B=True
                Else
                    If IsLimit(BC,x,y)=False Then B=False
                End If
                BC.SetARGB(x,y,aColor)
            End If
            If b Then BC.SetARGB(x,y,aColor)
        Next
    Next

End Sub

This sub refers to the other two of the previous post.
Unfortunately this procedure is slower than canvas, but I have to try other more efficient algorithms
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Circle

B4X:
Private Sub DrawCircleEmpty(BC As BitmapCreator, x As Int, y As Int, Radial As Int , BorderColor As Int)
    Dim X1 As Double = X + Radial * CosD(0)
    Dim Y1 As Double = Y + Radial * SinD(0)
  
    For i=1 To 720
        Dim X2 As Double = X + Radial * CosD(i/2)
        Dim Y2 As Double = Y + Radial * SinD(i/2)
        Drawline(BC,X1,Y1,X2,Y2,BorderColor)
        X1 = X2
        Y1 = Y2
    Next
End Sub

Arc
B4X:
Private Sub DrawArcEmpty(BC As BitmapCreator, x As Int, y As Int, Radial As Int, StartDegree As Int, EndDegree As Int,BorderColor As Int)
    Dim X1 As Double = X + Radial * CosD(StartDegree)
    Dim Y1 As Double = Y + Radial * SinD(StartDegree)
  
    Drawline(BC,X1,Y1,X,Y,BorderColor)
    For i=(StartDegree*2)+1 To EndDegree*2
        Dim X2 As Double = X + Radial * CosD(i/2)
        Dim Y2 As Double = Y + Radial * SinD(i/2)
        Drawline(BC,X1,Y1,X2,Y2,BorderColor)
        X1 = X2
        Y1 = Y2
    Next
    Drawline(BC,X1,Y1,X,Y,BorderColor)
End Sub

In the cycle I put the degrees from 0 to 720, but I divide by 2, so to get 0-360 with step 0.5.

The higher the value of the greater radius must be the precision then decrease the step of the cycle, otherwise we see the steps between the lines.
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Draw Bitmap to Trapezoid Form
upload_2018-6-21_17-46-54.png

We normally draw the images into rectangles and save them in a byte array, which is similar to a rectangle.
But sometimes there is the need to design an image that is born rectangular inside a trapezium and/or rotate them, as in the case of perspectives. (see image)
How to do?
I tried several algorithms found on the internet and graduated tsins on the graphics, but they all had bugs. So I created one myself, it's not the most performing,, but it works and does not crash.

B4X:
Type Point_Type(X as Int, Y as int)

Private Sub Trapezoid(bmp As BitmapCreator, BitTrap As BitmapCreator, p1 As Point_Type, p2 As Point_Type, p3 As Point_Type, p4 As Point_Type ) 'As B4XBitmap
    Dim XmaX As Int = bmp.mWidth-1
    Dim Ymax As Int = bmp.mHeight-1
    Dim Ystart,Xstart,Yend, Xend As Float
    Dim YstartNext,YendNext,Ynext,XstartNext As Float
    Dim XNow,YNow,XendNext,Xnext,xg As Float
    Dim Rec As B4XRect

        For Y = 0 To Ymax
       
            Ystart=p1.Y+((p3.Y-p1.Y)*y/Ymax)
            Yend=p2.Y+((p4.Y-p2.Y)*Y/Ymax)
            YstartNext=p1.Y+((p3.Y-p1.Y)*(y+1)/Ymax)
            YendNext=p2.Y+((p4.Y-p2.Y)*(Y+1)/Ymax)
       
            Xstart=p1.X+((p3.X-p1.X)*y/Ymax)
            Xend=p2.X+((p4.X-p2.X)*Y/Ymax)
            XstartNext=p1.X+((p3.X-p1.X)*(y+1)/Ymax)
            XendNext=p2.X+((p4.X-p2.X)*(Y+1)/Ymax)
   
            For X=0 To XmaX
                XNow=Xstart+(((Xend-Xstart)*x/XmaX))
                YNow=Ystart+(((Yend-Ystart)*x/XmaX))
                Xnext=XstartNext+(((XendNext-XstartNext)*(x+1)/XmaX))
                Ynext=YstartNext+(((YendNext-YstartNext)*(x+1)/XmaX))
                If Abs(XNow-Xnext)<1dip Then Xnext=XNow+sign(Xnext-XNow)*1dip
                If Abs(YNow-Ynext)<1dip Then Ynext=YNow+sign(Ynext-YNow)*1dip

                If XNow>Xnext Then
                    xg=XNow
                    XNow=Xnext
                    Xnext=xg
                End If
                   
                If YNow>Ynext Then
                    xg=YNow
                    YNow=Ynext
                    Ynext=xg
                End If
                Rec.Initialize(XNow+BitTrap.TargetRect.CenterX,YNow+BitTrap.TargetRect.CenterY,Xnext+BitTrap.TargetRect.CenterX,Ynext+BitTrap.TargetRect.CenterY)
                BitTrap.FillRect(bmp.GetColor(X,Y),Rec)
            Next
        Next
End Sub
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Edit: BitmapCreator was updated with many drawing methods: https://www.b4x.com/android/forum/threads/b4x-drawing-with-bitmapcreator.98887/

A small change that can significantly improve the performance:
B4X:
Private Sub Drawline(bc As BitmapCreator, x1 As Int, y1 As Int, x2 As Int, y2 As Int, Color As Int)
   Dim Dx,Dy,Diff As Int
   Dim x,y As Float
     Dim a As ARGBColor
   bc.ColorToARGB(Color, a)
   Dx = x2 - x1
   Dy = y2 - y1
   Diff=Max(Abs(Dx),Abs(Dy))
   x = x1
   y = y1
   Dim tx = Dx / Diff, ty = Dy / Diff As Float
   For D=0 To Diff
       If (x>=0 And x<bc.mWidth) And (y>=0 And y<bc.mHeight) Then
           bc.SetARGB(x, y, a)
       End If
       x = x + tx
       y = y + ty
   Next
End Sub
There are two changes:
1. Using SetARGB is faster than SetColor though at least in B4J the difference is not large.
2. You should do as few as possible calculations inside the loop.

This is still not the most optimized solution.
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Note that BitmapCreator.FillRect is much faster (x8 in B4J, probably more in B4i) than the DrawRec method with Filled = True.
Surely it is faster, I should be able to access the bitmapCreator ByteData to speed up all the functions.
But I think it's not possible, or am I wrong?

If it were possible to rewrite the functions with faster methods.
 

Star-Dust

Expert
Licensed User
Longtime User
A small change that can significantly improve the performance:
B4X:
Private Sub Drawline(bc As BitmapCreator, x1 As Int, y1 As Int, x2 As Int, y2 As Int, Color As Int)
   Dim Dx,Dy,Diff As Int
   Dim x,y As Float
     Dim a As ARGBColor
   bc.ColorToARGB(Color, a)
   Dx = x2 - x1
   Dy = y2 - y1
   Diff=Max(Abs(Dx),Abs(Dy))
   x = x1
   y = y1
   Dim tx = Dx / Diff, ty = Dy / Diff As Float
   For D=0 To Diff
       If (x>=0 And x<bc.mWidth) And (y>=0 And y<bc.mHeight) Then
           bc.SetARGB(x, y, a)
       End If
       x = x + tx
       y = y + ty
   Next
End Sub
There are two changes:
1. Using SetARGB is faster than SetColor though at least in B4J the difference is not large.
2. You should do as few as possible calculations inside the loop.

This is still not the most optimized solution.
This is also true. I did not optimize it because the result was equally satisfying (for me :p)

What needs significant improvements is the DrawPathFill, I was focusing on that. I have tried many algorithms but all not very efficient for a mobile device.
 

Star-Dust

Expert
Licensed User
Longtime User
I realized it was read-only in the sense that you could not change the bytes of the array :confused:
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I realized it was read-only in the sense that you could not change the bytes of the array
It will not help with the above methods. SetColor / SetARGB directly change this array. It is possible to set a single pixel a bit faster with CopyPixel however it will not have a large impact.

The algorithm you are using for DrawPathFill is very inefficient. You shouldn't call GetColor at all. You should instead calculate all the limits while drawing the lines.
However a probably better solution is to draw with B4XCanvas and then convert the bitmap to BitmapCreator.
 

Star-Dust

Expert
Licensed User
Longtime User
You should instead calculate all the limits while drawing the lines.
However a probably better solution is to draw with B4XCanvas and then convert the bitmap to BitmapCreator.
Already tried and it is slightly slower.
The thing that slows down more is the transition from B4Xcanvas to BitmapCreator.

I will try to implement your suggestions and see what I get.
Furthermore, for the remediation I found more efficient algorithms. I have yet to test them
 

Star-Dust

Expert
Licensed User
Longtime User
I used this code with B4XCanvas.

I also thought of generating a Canvas only for the area of the polygon.
For design the rubik's cube takes 0.20 - 0.36 sec
ScanLine Algoritm,that I use in my code, with the changes you suggested, takes 0.15 - 0.20 sec for design the rubik's cube

B4X:
Private Sub DrawPathFill2(BC As BitmapCreator,PointList As List, Color As Int)
    Dim P As Point_Type = PointList.Get(PointList.Size-1)
    Dim MinX As Int = P.X
    Dim MaxX As Int = P.X
    Dim MinY As Int = P.Y
    Dim MaxY As Int = P.Y
 
 
    For Each P As Point_Type In PointList
        MinX=Min(MinX,P.X)
        MaxX=Max(MaxX,P.X)
        MinY=Min(MinY,P.y)
        MaxY=Max(MaxY,P.y)
    Next
 
    Dim V As B4XView = xui.CreatePanel("")
    V.SetLayoutAnimated(0,0,0,Abs(MaxX-MinX)+1,Abs(MaxY-MinY)+1)

    Dim Can As B4XCanvas
    Can.Initialize(V)
    Dim Rec As B4XRect
    Rec.Initialize(0,0,Abs(MaxX-MinX)+1,Abs(MaxY-MinY)+1)
    
    Dim Path As B4XPath
    Dim P As Point_Type = PointList.Get(PointList.Size-1)
 
    Path.Initialize(P.X-MinX,P.Y-MinY)
 
    For Each P As Point_Type In PointList
        Path.LineTo(P.x-MinX,P.y-MinY)
    Next
 
    Can.DrawPath(Path,Color,True,1)
    Rec.Initialize(MinX,MinY,MaxX,MaxY)
    BC.DrawBitmap(Can.CreateBitmap,Rec,False)
End Sub

At the moment although my algorithm is inefficient it is better than Canvas.

I will try to use the FloodFill algorithm, which they say is better. is bad and slower.

If I fall below 0.10 sec for me it is already a goal. The ideal would be to design it 0.02-0.04 sec

In the previous version of my library I used only B4XCanvas (for polygon) and BitmapCreator (for Images) used 0.02-0.04 for the rubik's cube and 0.16-0.19 for the image.
Using BitmapCreator I take 0,15-0,19 for the Rubik's Cube and 0,07-0,09 for the images
I gained in the immgini but I lost in the polygons.
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Update:
I go a little bit off the topic, but to better explain the question.
I modified my library, where I use this sub, for a better compromise between Canvas and BitmapCreator.

Now i'm using B4XCavas to the sight and I draw all the polygons. For images, I create a BitmapCreator with the image size, I process the pixels and I design the BitmapCreator in the Cavas and I'm getting better performance.

Previously I turned the entire B4XCavas into Bitmap and then Bitmap into BitmapCreator. I made the process to the image and finally I drew the BitmapCreator resulting in the Canvas. This was extremely slow.

Now the times are these:

Rubik's cube: 0,02-0,04 sec
Image: 0.04-0.06 sec


Conclusions:
  1. Having a BitmapCreator as big as View,helps if you have to draw images, but slow down with the polygons full.
  2. Having a View/B4XCanvas, helps the polygons but slows down the images.
  3. Having a BitmapCreator as big as the view and drawing images, but use Canvas for polygons, is discreet compromise.
  4. Using a view/B4XCanvas to draw polygons and creating little BitmapCreator for images to copy on canvas is better
Now I return to the main topic as the title
 
Last edited:
Top