Recent

Author Topic: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]  (Read 989 times)

DMH

  • Newbie
  • Posts: 2
I am a relative newcomer to Lazarus IDE.  As a learning exercise I have recently developed a prototype Mandelbrot Set Explorer program (MSE) using Lazarus.  Read about it on my website at https://www.davidhaycraft.id.au/DMHPROG.

MSE first generates an 800X600 pixel array depicting a user-selected region of the Mandelbrot Set  in

   Drawing: array[1..800,1..600]of TColor

It then calls procedure TForm1.FormPaint() to display the drawing using

   for px:=1 to 800 do for py:=1 to 600 do
      Form1.Canvas.Pixels[px,py] := Drawing[px,py];

This loop takes over 5 seconds to complete on my Windows 11 desktop PC!

Is there a faster way for a Lazarus IDE program to transfer graphic information from a 2D array of points to the screen?

Any constructive advice would be welcome :)

Regards to all

DMH
Canberra, Australia




 

Thausand

  • Sr. Member
  • ****
  • Posts: 445
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #1 on: November 08, 2025, 10:59:57 am »
Is there a faster way for a Lazarus IDE program to transfer graphic information from a 2D array of points to the screen?
Yes, is (other) way :)

Make not use array. Use bitmap pixel draw screen-off. Then if picture finish (formpaint) then 'blit' canvas. Bitmap can be bgrabitmap or other 2d draw solution if want (is require how work solution implementation detail and may be hard if not know).

https://wiki.freepascal.org/Developing_with_Graphics

PS: there special forum talk for multimedia (have forum graphic): https://forum.lazarus.freepascal.org/index.php/board,57.0.html
« Last Edit: November 08, 2025, 11:05:03 am by Thausand »

paweld

  • Hero Member
  • *****
  • Posts: 1521
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #2 on: November 08, 2025, 11:32:05 am »
As @Thausand write - use a bitmap, not an array.
See sample in attachment.
« Last Edit: November 08, 2025, 11:33:58 am by paweld »
Best regards / Pozdrawiam
paweld

wp

  • Hero Member
  • *****
  • Posts: 13267
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #3 on: November 08, 2025, 04:06:53 pm »
In my sample code you store your data directly in a 2D-array and then use this data array in a TRawImage as pixel matrix. A TBitmap has a method to load itself from such a RawImage, and then the image can easily painted by standard LCL functions. See attached demo.

Maybe one note (because I fell into this trap): If you are expecting the pixels in the image to be arranged such that x runs horizontally and y vertically the array must be declared in reverse order, array[y, x] (or array[y] of array[ x ] ) because the TRawImage wants the pixels to be arranged along the rows, or in other words, x must correspond to the right-most index of the 2d array.
« Last Edit: November 08, 2025, 05:34:43 pm by wp »

TBMan

  • Sr. Member
  • ****
  • Posts: 305
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #4 on: November 08, 2025, 04:42:10 pm »
WP is right. When I write my small bitmap arrays to screen I do this

Code: Pascal  [Select][+][-]
  1. var
  2. colorarray:array[0..31,0..23] of byte;
  3. x,y,r,c:integer;
  4.  
  5. begin
  6. // assuming something filled the colorarray
  7.  
  8. x := 100; y := 100;
  9.  
  10. for r := 0 to 31 do
  11.   for c := 0 to 23 do
  12.     putpixel(x+c,y+r,colorarray[r,c]);  // old school Borland graph routine
  13.  
  14. // also quite a bit faster is the use of a transparent color - I usually use 0 as the non displayed pixel, but it depends on the // palette I'm using, but for small "bitmaps" it's not that big of a deal. In your case it might be helpful if you aren't
  15. // animating the bitmap (if so then the area where the bitmap is being plotted has to be cleared first).
  16.  
  17. for r := 0 to 31 do
  18.    for c := 0 to 23 do
  19.      if colorarray[r,c] <> 0 then putpixel(x+c,r+y,colorarray[r,c]);
  20. end;
  21.  

And the other mentioned is the "page flipping" by writing to a canvas not viewed, then moving the whole canvas at once to the visual canvas. That's been a "trick" since the beginning of time.... 

I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

paweld

  • Hero Member
  • *****
  • Posts: 1521
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #5 on: November 08, 2025, 06:09:43 pm »
@wp: it's still slower than operating on a bitmap from the beginning
Best regards / Pozdrawiam
paweld

wp

  • Hero Member
  • *****
  • Posts: 13267
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #6 on: November 08, 2025, 06:34:20 pm »
This comparison is a bit "unfair": case 2 draws a bitmap which has been painted by standard canvas techniques but not from an data buffer array as requested by the OP. And the processes of drawing into the bitmap has been excluded from the measurement loop in case 2.

Of course the OP should paint the results of the Mandelbrot calculation directly into the bitmap, but not by means of setting bitmap.Pixels[x,y], but by using Scanline techniques. That's the fastest way to do it - but also the one which is most difficult and error-prone. An intermediate array buffer, or a helper LazIntfImage, or even a BGRABitmap, is much easier to handle and still quite fast.

paweld

  • Hero Member
  • *****
  • Posts: 1521
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #7 on: November 09, 2025, 06:16:11 am »
Quote from: wp
Of course the OP should paint the results of the Mandelbrot calculation directly into the bitmap
That's exactly what I meant, to keep the image in a bitmap and draw it directly onto the canvas.
Quote
but not by means of setting bitmap.Pixels[x,y], but by using Scanline techniques
I think your example would be a very good solution here, i.e., converting the array to a bitmap using TRawImage. But I would recommend doing this immediately after generating the array, rather than in the OnPaint event.
Best regards / Pozdrawiam
paweld

wp

  • Hero Member
  • *****
  • Posts: 13267
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #8 on: November 09, 2025, 12:32:12 pm »
I think your example would be a very good solution here, i.e., converting the array to a bitmap using TRawImage. But I would recommend doing this immediately after generating the array, rather than in the OnPaint event.
It depends... If the bitmap is created outside the OnPaint event the same information is stored twice: in the array and in the bitmap. Well, the user could dispose the array after creating the bitmap - but maybe the array is the primary data needed, why drop it then? It's always about the same conflict: speed vs memory. And why not store the results of the Mandelbrot calculation directly in the bitmap? Maybe the calculation is performed on a non-LCL system where TBitmap does not exist? There are many open questions, we simply don't know all constraints and circumstances.

Roland57

  • Hero Member
  • *****
  • Posts: 529
    • msegui.net
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #9 on: November 09, 2025, 02:00:49 pm »
See attached demo.

Here (under Linux), nothing happens when I click on the button: the window stays empty. (Tested with two different versions of Lazarus, 3.6 and 4.99.)
My projects are on Codeberg.

paweld

  • Hero Member
  • *****
  • Posts: 1521
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #10 on: November 09, 2025, 02:12:56 pm »
Quote from: Roland57
Here (under Linux), nothing happens when I click on the button: the window stays empty. (Tested with two different versions of Lazarus, 3.6 and 4.99.)
On Linux change init to ARGB (not RGBA):
Code: Pascal  [Select][+][-]
  1. rawImg.Description.Init_BPP32_A8R8G8B8_BIO_TTB(XCOUNT, YCOUNT);
Best regards / Pozdrawiam
paweld

wp

  • Hero Member
  • *****
  • Posts: 13267
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #11 on: November 10, 2025, 01:09:51 am »
Oh man, why must graphics be so frustrating sometimes...

It is the purpose of the RawImage.Description.Init_XXXX procedure to define the order of the red, green, blue, and alpha channel bytes in the pixels. In my code, using an array of TColor, I am putting values into the data array which have red in the least-signficant byte 0, green in byte 1, blue in byte 2, and the alpha (transparency) channel in the most-significant byte 3 - because we're working with 32-bit colors. This is important information for the bitmap drawing procedure to extract the color channels of each pixel from these array values in the order needed.

Why is the Linux output empty? I think, this is because of the alpha channel. In TColor the alpha byte of "normal" (= non-system) colors is zero.Windows did not use alpha for a long time and thus is ignoring it in its default GDI drawing methods (used by the LCL TBitmap). In Linux, however, the alpha channel always has been there and is being used for painting. And Alpha = 0 means: fully transparent!

How to fix it? I tested paweld's idea: it does display something in the paintbox now, but the colors are changed. No, I think the lsb-to-msb order of RGBA is correct, but we only must provide a correct (=opaque) alpha channel. This can be done by an extended RGBToColor function which explicitely sets all bits in the alpha channel ($FF):
Code: Pascal  [Select][+][-]
  1.   function OpaqueColor(r, g, b: Byte): DWord;
  2.   begin
  3.     Result := r + g shl 8 + b shl 16 + $FF shl 24;
  4.   end;
Of course, this is no real "TColor" any more, it works only in conjunction with pf32bit bitmaps where the RawImage.Description provides the information where the color channels can be found. Assigning it to a Brush.Color or Pen.Color will result in some different color because the LCL interprets the lsb as indicator of system colors. Therefore it is better to store some "neutral" type in the data array, such as a DWord.

See attached modified project which works in Win, Linux gtk2/gtk3/t5/qt6, cocoa.

DMH

  • Newbie
  • Posts: 2
Re: Drawing a picture on the screen using Form1.Canvas.Pixels[px,py]
« Reply #12 on: November 10, 2025, 05:01:07 am »
Thanks to the many Lazarus Forum members who replied to my question about a high-speed method to transfer graphic information from a 2D array of points to the screen.

The response that I found most helpful was from Hero Member "wp" who recommended the following approach:
1. initialise a TRawImage object and link it to my existing 2D array of TColor points
2. create a TBitmap object and load it from the step1 raw image
3. place a PaintBox component on my main form and use PaintBox1.Canvas.Draw() to display the step2 bitmap on screen.

Especially useful was his provision of a downloadable file DrawMatrix.zip  containing a neat little Lazarus demo of this technique.

I copied his code almost verbatim, installed it in my Lazarus program Mandelbrot Set Explorer (MSE) and got instant gratification - subsecond response times. See attached screenshot.

Cheers
DMH, Canberra Australia


 

TinyPortal © 2005-2018