B4J Question Rotated rectangle on B4XCanvas

max123

Well-Known Member
Licensed User
Longtime User
Hi everyone,

I'm converting my B4J application where I normally used a Canvas to use B4XCanvas.
Canvas has the DrawRectRotated method that I used but can't find this method in B4XCanvas.

I understand that I could use DrawBitmapRotated and create a runtime image (using BitmapCreator) by filling it pixel by pixels of the desired color, but is this the right way to draw the rotated rectangle as quickly as possible?

My application is time critical because it receives fast commands via WiFi (commands that draw) and I need that for performance reasons the rectangle be drawn as fast possible, before I receive next packet.

Is there any command using B4XCanvas which is the counterpart of Canvas DrawRectRotated?

I have two versions of my app, for B4J and for B4A so I need it work on both.

Many thanks
 
Last edited:

sorex

Expert
Licensed User
Longtime User
are you testing in release mode or debug?

also change the sub type to normal and remove the return value and sleep.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Release mode, but I can now see that my code is not right, I've adapted mine, but use Wait For, and I do not pass rect and path as arguments, the rect and path are globally declared, now I will try your exact code, the second one because the first seem to not applicable to my use case, but initializing a global Rectangle to 0,0,0,0 and then change Left, Top, Right, Bottom properties I think it can work. But I even try with DrawPolygon as Erel said.

Many thanks to both. 😉
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
No way here without use Wait For and without Sleep(0), the log returns 78 milliseconds, but the app hang, the form do not show it's content, after all rectangles are drawn on B4XCanvas (about 15 seconds) the form appears with rectangle on Canvas as for my first tests.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
This is my code:
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    
    Dim xui As XUI
    
    Dim cvs As Canvas
    Dim xuiCvs As B4XCanvas
    
    Private Pane1 As Pane
    Private Pane2 As Pane   
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("1") ' Load the layout file.
    MainForm.Show
    
    cvs.Initialize("")
    Pane1.AddNode(cvs,0,0,Pane1.PrefWidth, Pane1.PrefHeight)
    cvs.DrawRect(0,0, cvs.Width, cvs.Height, fx.Colors.Black, True, 0)
    cvs.DrawText("Canvas", 10, 25, fx.DefaultFont(20), fx.Colors.Cyan, "LEFT")
    
    xuiCvs.Initialize(Pane2)
    Dim rect As B4XRect
    rect.Initialize(0, 0, xuiCvs.TargetRect.Width, xuiCvs.TargetRect.Height)
    xuiCvs.DrawRect(rect, xui.Color_Black, True, 0)
    xuiCvs.DrawText("B4XCanvas", 10, 25, xui.CreateDefaultFont(20), xui.Color_Cyan, "LEFT")
    
    Dim start, elapsed As Long

'    start = DateTime.Now
'    For i = 1 To 500
'        Wait For (DrawRectRotatedCanvas) Complete()
'    Next
'    elapsed = DateTime.Now - start
'    Log("Draw 500 rotated rectangles on Canvas required " & elapsed & " Milliseconds")
    
'    rect.Initialize(100, 100, 300, 400)
'    path.InitializeRoundedRect(rect, 0)
    
    Dim path As B4XPath
    Dim rect As B4XRect
    rect.Initialize(100, 100, 300, 400)
    path.InitializeRoundedRect(rect, 0)
    
    start = DateTime.Now
    For i = 1 To 500
        DrawRectRotatedB4XCanvas(rect,path)
    Next
    elapsed = DateTime.Now - start
    Log("Draw 500 rotated rectangles on B4XCanvas required " & elapsed & " Milliseconds")
End Sub

Sub DrawRectRotatedCanvas()
    cvs.DrawRectRotated(100, 100, 200, 300, fx.Colors.Red, True, 0, 45)
End Sub

Sub DrawRectRotatedB4XCanvas(rect As B4XRect, path As B4XPath)
    xuiCvs.DrawPathRotated(path, xui.Color_Blue, True, 0, 45, rect.CenterX, rect.CenterY)
End Sub

Many thanks
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
I don't see any problem?

Draw 500 rotated rectangles on B4XCanvas required 16 Milliseconds
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
. Draw 500 rotated rectangles on B4XCanvas required 16 Milliseconds

Here hangs on my DualCore PC, but my system is pretty old and even that I still working on Windows XP

I've tried DrawPolygons and is fast, require 204 millliseconds to draw 500 rectangles as explayned by Erel on his post
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
that it is slower on a slower system is normal but 1000 times seems to be too much.

you better go for one of the 2 alternatives then.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
DrawPoligon is fast, but it not a replace of DrawRectRotated, so the problem now is how to calculate points for a rotation and size, a think this involves Sine and Cosine, so it return to be slow, I'm confused
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I've attached the zip files of 2 projects:

- DrawRectRotated without WaitFor and Sleep(0) that hangs on my pc on startup as explained, after 15 secs I see the right Form content with Canvases and a Blue rectangle on B4XCanvas, because my machine is sure a lot slow of Your, maybe try to increase the 500 to 5000 or even 50000

- The other project I use DrawPolygon but I do not know how to set right poitnts to pass to DrawPolygon function

Another time many thanks 😉
 

Attachments

  • DrawRectRotated.zip
    2.3 KB · Views: 172
  • DrawRectRotated_DrawPolygon.zip
    2.8 KB · Views: 178
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Result here:
Draw 500 rotated rectangles on B4XCanvas required 11 Milliseconds.
These logs are not 100% correct because the screen doesn't update immediately. On my computer it is very fast and doesn't require any optimization.

Do you really need to draw 500 rectangles? Have you encountered a performance issue in your real use case?
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Your computer is Super fast, wow 😍 and mine is super slow 😏

Do you really need to draw 500 rectangles? Have you encountered a performance issue in your real use case?

No, I will just compare results to search a best speed solution, to test it better I will to test directly using ESP8266 VirtualDisplay library on the go to see if it is slow or pretty fast.

Now I've just an option, use DrawPoligon, all others are too slow to test my app on my PC, I have even Raspberry PI3 to test but OpenJavaFX is very slow.

An so, what about rotate the rectangle using DrawPolygon? You can make just an example code so I can test and implement inside my app, all other Canvas methods works (at least all compile, need to test with ESP, these already tested using normal Canvas) and stayed unchanged from normal Canvas.

These are Canvas methods I wrapped:
GetWidth
GetHeight
ClearScreen, ClearScreenRGB, ClearScreenARGB
Refresh
SetPixel, SetPixelRGB, SetPixelARGB
DrawLine, DrawLineRGB, DrawLineARGB
DrawCircle, DrawCircleRGB, DrawCircleARGB
Print
DrawText, DrawTextRGB, DrawTextARGB
DrawTextRotated, DrawTextRotatedRGB, DrawTextRotatedARGB
DrawRect, DrawRectRGB, DrawRectARGB
------> DrawRectRotated, DrawRectRotatedRGB, DrawRectRotatedARGB
DrawOval
DrawImage

Many thanks
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I don't see any reason to make these unreal measurements and unreal optimizations.
Start with B4XCanvas.DrawPath. If it is too slow in your real use case then switch to Canvas.DrawRectRotated.

As I explained I swithed from normal Canvas to B4XCanvas not for speed issues, but because it have the Invalidate method that I need, this work well on Android with B4A, the poroblem is that even the B4XCanvas draw itself the content even without call the Invalidate method, so need to collect any received command inside a List or any collection and draw them when Invalidate command is received.

Many thanks
 
Last edited:
Upvote 0

sorex

Expert
Licensed User
Longtime User
I explained before how you can bypass that by using 2 canvas objects.

If your invalidate command is so important then draw as the commands come in in a hidden canvas and swap them when the invalidate command comes in.

I don't know what you are trying to display but 500 rotated rectangles doesn't seem to be like a real case.
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Maybe you could bypass the same concept of "invalidate" (if applicable to your problem):
Think of the list of commands to execute as a FIFO buffer. Then, when the buffer reaches a given threshold (e.g. 50 commands) do execute them and so on until the buffer gets empty (or eventually go on forever). To avoid any possible lag, you could execute commands in buffer even on a lower quantity after some time from last received command.

The above could even mixed with the Invalide command. It could go like this:
- receive commands and store them in FIFO buffer
- receive Invalidate command
- start executing buffer commands while the listening sub adds new commands to the buffer
This way the Invalidate command acts like a start/stop signal while the drawing could be executed at paces compatible with ypur hardware (i.e. in smal batches)

Just my 2c
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
As I explained I swithed from normal Canvas to B4XCanvas not for speed issues, but because it have the Invalidate method that I need
But I've already answered that B4XCanvas = Canvas. The behavior is identical. It is just a cross platform API layer.
The Invalidate method doesn't do anything in B4J. It is there because it is required in B4A and B4i.
 
Upvote 0
Top