RGBSurface instead of Graphics.Pixel

I just read that Graphics.Pixel is deprecated and you should use RGBSurface instead:
Graphics.Pixel

How can I use RGBSurface on a Graphics object (e.g. Paint event of a canvas)?
Do I always have to create a picture? Would this reduce the performance?

Any ideas?

Just create a picture. Performances should not be changed, except if a very, very big picture.

Please see this sample project.
Creating a picture and using the RGBSurface is about 10 times slower than simply drawing a pixel on the Graphics object.
Any ideas for optimization?
Or simply use FillRect 1, 1, 1, 1?

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint

[code] Dim u As Integer = 100

For i As Integer = 1 To u

Dim p As New Picture(1, 1)
p.RGBSurface.Pixel(0, 0) = &cff0000

g.DrawPicture p, i, i

Next
End Sub[/code]

Why do you put the New Picture and g.DrawPicture p, i, i lines in the Loop ? That is taking considerable time.

If I am awake enough…

What do you really want to do ?

[quote=336916:@Emile Schwarz]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint

[code] Dim u As Integer = 100

For i As Integer = 1 To u

Dim p As New Picture(1, 1)
p.RGBSurface.Pixel(0, 0) = &cff0000

g.DrawPicture p, i, i

Next
End Sub[/code]

Why do you put the New Picture and g.DrawPicture p, i, i lines in the Loop ? That is taking considerable time.

If I am awake enough…

What do you really want to do ?[/quote]

Even if I use it like this:

[code]
Dim u As Integer = 100
Dim p As New Picture(g.Width, g.Height)

For i As Integer = 1 To u

p.RGBSurface.Pixel(i, i) = &cff0000

Next

g.DrawPicture p, 0, 0[/code]

It’s about 2 times slower than using Graphics.Pixel.
As Graphics.Pixel is deprecated I simply want a good and performant alternative :-).

I just realized the error is in the impleùmentatio: yiou use Refresh in the loop’s button. That is what tooks so long.

Place an offscreen Picture, set the code to it and in the end, draw that Offscreen Picture in Canvas2.Paint.

In fact, tell what you really want to do: my advice above can be useless in some cases…

ERRORS BELOW. When I changed i, I in the last line to 0,0 it works.

I cannot run your modified code below (xojo 2017r1.1):
This item does not exists.

No yellow hint given.

Canvas2:

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
Dim u As Integer = 100
Dim p As New Picture(1, 1)

For i As Integer = 1 To u
p.RGBSurface.Pixel(0, 0) = &cff0000
Next

g.DrawPicture p, i, i
End Sub[/code]

[quote=336919:@Emile Schwarz]a. I cannot run your modified code below (xojo 2017r1.1):
This item does not exists.

No yellow hint given.

Canvas2:

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
Dim u As Integer = 100
Dim p As New Picture(1, 1)

For i As Integer = 1 To u
p.RGBSurface.Pixel(0, 0) = &cff0000
Next

g.DrawPicture p, i, i
End Sub[/code][/quote]

Strange … I can run it with this code:

Yes Stephan, I discovers that and your post appears when I click in Post a Reply.

Add a property of type Picture names MyPict,

and in PushButton2:

[code]Sub Action() Handles Action
Dim u As Integer = 100

MyPict = New Picture(Canvas2.Width, Canvas2.Height)

For i As Integer = 1 To u
For j As Integer = 1 To u
MyPict.RGBSurface.Pixel(i, j) = &cff0000
Next
Next

Canvas2.Refresh
End Sub
[/code]

[quote=336919:@Emile Schwarz]I just realized the error is in the impleùmentatio: yiou use Refresh in the loop’s button. That is what tooks so long.

Place an offscreen Picture, set the code to it and in the end, draw that Offscreen Picture in Canvas2.Paint.

In fact, tell what you really want to do: my advice above can be useless in some cases…

ERRORS BELOW. When I changed i, I in the last line to 0,0 it works.

I cannot run your modified code below (xojo 2017r1.1):
This item does not exists.

No yellow hint given.

Canvas2:

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
Dim u As Integer = 100
Dim p As New Picture(1, 1)

For i As Integer = 1 To u
p.RGBSurface.Pixel(0, 0) = &cff0000
Next

g.DrawPicture p, i, i
End Sub[/code][/quote]

I simply want an easy and performant alternative for Graphics.Pixel (as this is deprecated).
The loops and Canvas.Refresh and methods used in the sample project were only for testing the performance of alternative techniques.
Even this is two times slower than simply using Graphics.Pixel:

[code]
Dim u As Integer = 100
g.ForeColor = &cff0000

For i As Integer = 1 To u
g.fillrect i, i, 1, 1
Next[/code]

So what do you suggest is the best way to draw let’s say 100 pixels anywhere on a canvas?
If possible it should be as performant as Graphics.Pixel.

If you want to watch the progress:

[code]Sub Action() Handles Action
Dim u As Integer = 100

MyPict = New Picture(Canvas2.Width, Canvas2.Height)

For i As Integer = 1 To u
For j As Integer = 1 To u
MyPict.RGBSurface.Pixel(i, j) = &cff0000
Next
Canvas2.Refresh
Next
End Sub[/code]

You can even have fun with it:

[code]Dim u As Integer = 100
Dim i,j As Integer
MyPict = New Picture(Canvas2.Width, Canvas2.Height)

For i = 1 To u Step 2
For j = 1 To u Step 2
MyPict.RGBSurface.Pixel(i, j) = &cff0000
Next
Next

Canvas2.Invalidate[/code]

Nice pattern ?

Note: Step 2 in the For line and I Dim the x,y variables outside of that line.

Yes, Xojo docs uses For i As Integer = 1 To u, but I do not love it. And it certainly takes more time to be executed.

BTW: I forgot the code in Canvas2.Paint:

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint If MyPict <> Nil Then g.DrawPicture MyPict,0,0 End If End Sub

[quote=336927:@Emile Schwarz]You can even have fun with it:

[code]Dim u As Integer = 100
Dim i,j As Integer
MyPict = New Picture(Canvas2.Width, Canvas2.Height)

For i = 1 To u Step 2
For j = 1 To u Step 2
MyPict.RGBSurface.Pixel(i, j) = &cff0000
Next
Next

Canvas2.Invalidate[/code]

Nice pattern ?

Note: Step 2 in the For line and I Dim the x,y variables outside of that line.

Yes, Xojo docs uses For i As Integer = 1 To u, but I do not love it. And it certainly takes more time to be executed.[/quote]

If you put this in a loop and do 1000 refreshes (invalidates) it is two times slower than simply drawing pixels:

[code]Sub Action() Handles Action

Dim u As Integer = 100
Dim i,j As Integer
MyPict = New Picture(Canvas2.Width, Canvas2.Height)

For i = 1 To u Step 2
For j = 1 To u Step 2
MyPict.RGBSurface.Pixel(i, j) = &cff0000
Next
Next

Dim StartTime As Double = Microseconds
Dim uu As Integer = TextField1.Text.Trim.Val

For k As Integer = 1 To uu
Canvas2.Invalidate
Next

Dim EndTime As Double = Microseconds

Label2.Text = CSTr((EndTime - StartTime) / 1000) + " ms"
End Sub
[/code]

  1. Check the old posts here about RGBSurface and set it outside of the loop.
  2. disable background tasks during the loop

Here is an example where “ta” is a TextArea:

[code] #Pragma BackgroundTasks false

const maxCount = 1000
dim s as Double
dim p as Picture
dim r As RGBSurface

s = Microseconds
p = new Picture(maxCount+1, maxCount+1)
For i As Integer = 0 To maxCount
For j As Integer = 0 To maxCount
p.RGBSurface.Pixel(i, j) = &cff0000
Next
Next
s = Microseconds - s
ta.AppendText "Time = " + str(s/1000) + EndOfLine

s = Microseconds
p = new Picture(maxCount+1, maxCount+1)
r = p.RGBSurface
For i As Integer = 0 To maxCount
For j As Integer = 0 To maxCount
r.Pixel(i, j) = &cff0000
Next
Next
s = Microseconds - s
ta.AppendText "Time = " + str(s/1000) + EndOfLine
[/code]

This should be much faster.

Stephan:
Why do you want to do that ?

New Canvas2 Paint code, no button (you can add ellapsed time computed and report it in a TexTField):

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
Dim u As Integer = Me.Width
Dim i,j As Integer
Dim p As New Picture(Me.Height, Me.Width)

For i = 1 To u Step 2
For j = 1 To u Step 2
p.RGBSurface.Pixel(j, i) = &cff0000
Next
Next

g.DrawPicture p, 0, 0

Canvas2.Invalidate
End Sub[/code]

Edit: and what about flickering under Windows ('cause you runs Windows) caused by Invalidate ?

[quote=336931:@Emile Schwarz]Stephan:
Why do you want to do that ?

New Canvas2 Paint code, no button (you can add ellapsed time computed and report it in a TexTField):

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
Dim u As Integer = Me.Width
Dim i,j As Integer
Dim p As New Picture(Me.Height, Me.Width)

For i = 1 To u Step 2
For j = 1 To u Step 2
p.RGBSurface.Pixel(j, i) = &cff0000
Next
Next

g.DrawPicture p, 0, 0

Canvas2.Invalidate
End Sub[/code]

Edit: and what about flickering under Windows ('cause you runs Windows) caused by Invalidate ?[/quote]

As mentioned the loops and so on are only for testing.
I want to see how long it takes the canvas to paint so I do a refresh a 1000 times.
If I put the code above in the paint event it takes 4 times longer:

Beside: “you are not intended to code like this” I do not know what to say.

Try setting two far larger Canvas to be able to compute statistically significant ellapsed times… instead of putting a refresh in the print loop.

[quote=336915:@Stefan Adelsberger]Please see this sample project.
Creating a picture and using the RGBSurface is about 10 times slower than simply drawing a pixel on the Graphics object.
Any ideas for optimization?
Or simply use FillRect 1, 1, 1, 1?[/quote]

This is an issue if you draw many times with pixel. You could also explore

g.penwidth = 1 g.Drawline(x,y, x+1, y)

And no offense, but your test is largely irrelevant, as it should be replaced by drawline for any real life application.