Recent

Author Topic: "waterfall" image  (Read 3890 times)

Rik

  • Jr. Member
  • **
  • Posts: 67
"waterfall" image
« on: October 22, 2018, 03:54:59 pm »
Hello,

I want to create an image where the lines shift down once a second and a new line is added at the top (behaves a bit like a waterfall).
With the code below in a timer it is easily done, but very slow (takes about 100 ms on my computer).
It should be possible to to this much faster by  moving memory blocks or using Bitmap.RawImage instead of Pixels[], but I can't figure out how.

Thanks,  Rik

Code: Pascal  [Select][+][-]
  1. with WaterfallImage do // = TImage
  2. begin // this works, but very slow (100 ms)
  3.   try
  4.     Picture.Bitmap.BeginUpdate(True); // without this it even takes 200 ms
  5.     for y:= Height-1 downto 1 do
  6.     begin
  7.       for x:= 0 to Width-1 do
  8.       begin
  9.         Canvas.Pixels[x,y]:= Canvas.Pixels[x,y-1];
  10.       end;
  11.     end;
  12.     for x:= 0 to Width-1 do
  13.     begin
  14.       Canvas.Pixels[x,0]:= NewLine[x];
  15.     end;
  16.   finally
  17.     Picture.Bitmap.EndUpdate(False);
  18.   end;
  19. end;
  20.  
« Last Edit: October 22, 2018, 04:59:12 pm by Rik »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8746
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: "waterfall" image
« Reply #1 on: October 23, 2018, 11:11:42 am »
Don't use TImage for animation, see $(LazarusDir)/examples/lazintfimage for reference on how to manipulate pixels fast.

Handoko

  • Hero Member
  • *****
  • Posts: 5129
  • My goal: build my own game engine using Lazarus
Re: "waterfall" image
« Reply #2 on: October 23, 2018, 11:37:43 am »
It should be possible to to this much faster by  moving memory blocks or using Bitmap.RawImage instead of Pixels[], but I can't figure out how.

Try using TImage.Picture.Bitmap.ScanLine. On my tests, it is much faster than single pixel accessing.
You can find the example code here:
http://forum.lazarus.freepascal.org/index.php/topic,37242

I think you will be interested to know:
http://wiki.freepascal.org/Fast_direct_pixel_access

Rik

  • Jr. Member
  • **
  • Posts: 67
Re: "waterfall" image
« Reply #3 on: October 23, 2018, 02:12:41 pm »
lazintfimage is what I needed, thank you for the hint.

In case someone is interested, here the code adapted for lazintfimage (takes less than 1 millisecond):

Code: Pascal  [Select][+][-]
  1. type
  2.   TRGBTripleArray = array[0..32767] of TRGBTriple;
  3.   PRGBTripleArray = ^TRGBTripleArray;
  4. var
  5.   x,y: word;
  6.   r1,r2: PRGBTripleArray;
  7.   Img: TLazIntfImage;
  8. begin
  9.   with WaterfallImage.Picture.Bitmap do
  10.   begin
  11.     try
  12.       Img:= CreateIntfImage;
  13.       for y:= Img.Height-1 downto 1 do // shift image 1 row down (waterfall)
  14.       begin
  15.         r1:= Img.GetDataLineStart(y-1);
  16.         r2:= Img.GetDataLineStart(y);
  17.         for x:=0 to Img.Width-1 do r2^[x]:= r1^[x];
  18.       end;
  19.       LoadFromIntfImage(Img);
  20.       BeginUpdate(True);
  21.       for x:= 0 to Width-1 do Canvas.Pixels[x,0]:= NewLine[x]; // add the new line at the top
  22.     finally
  23.       EndUpdate(False);
  24.     end;
  25.   end;
  26. end;
  27.  

Regarding Bitmap.ScanLine: I thought this works only in Delphi, not in Lazarus. But GetDataLineStart does pretty much the same.
« Last Edit: October 23, 2018, 02:20:18 pm by Rik »

circular

  • Hero Member
  • *****
  • Posts: 4195
    • Personal webpage
Re: "waterfall" image
« Reply #4 on: October 25, 2018, 11:34:11 am »
Indeed it seems GetDataLineStart works fine. I wonder though, how much time one cycle takes in total (image re-creation and display). Note that for very  quick things in milliseconds, you need to run the operation 15 times and then divide the resulting time by 15. Because the time is measured by lumps of 15 ms so if it takes 12 ms you will get either that it took 0 ms or 15 ms.

I wonder if you need to free the Img object.

In fact you could do a Move to copy one whole line at once:
Code: Delphi  [Select][+][-]
  1.   for y:= Img.Height-1 downto 1 do // shift image 1 row down (waterfall)
  2.     move(Img.GetDataLineStart(y-1)^, Img.GetDataLineStart(y)^, sizeof(TRGBTriple)*Img.Width );
Conscience is the debugger of the mind

 

TinyPortal © 2005-2018