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

Discussion in 'Code Snippets' started by Star-Dust, Jun 20, 2018.

Tags:
1. 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

2. DRAW LINE
Code:
`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)    NextEnd Sub`

DRAW PATH (Empty)
Code:
`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: Jun 21, 2018
3. DRAW PATH (FILL)
Algorithm ScanLine

Update 0.01
Code:
`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) = 01End SubPrivate 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    NextEnd 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: Jun 21, 2018
4. Circle

Code:
`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    NextEnd Sub`
Arc
Code:
`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: Jun 20, 2018
5. Draw Bitmap to Trapezoid Form 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.

Code:
`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        NextEnd Sub`

Last edited: Jun 21, 2018
6. A small change that can significantly improve the performance:
Code:
`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   NextEnd 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.

hibrid0, Peter Simpson and Star-Dust like this.
7. Note that BitmapCreator.FillRect is much faster (x8 in B4J, probably more in B4i) than the DrawRec method with Filled = True.

Star-Dust likes this.
8. 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.

9. This is also true. I did not optimize it because the result was equally satisfying (for me )

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.

10. BC.Buffer returns the internal array. Though I don't see how it will help with most of the above methods.

Star-Dust likes this.
11. I realized it was read-only in the sense that you could not change the bytes of the array 12. 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 likes this.
13. 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

14. If you like you can upload a small example and I'll try to optimize it.

Star-Dust likes this.
15. 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

Code:
`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: Jun 21, 2018
16. 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