7

I have made the following code:

procedure TForm15.Button1Click(Sender: TObject);
var
  Bitmap1: TBitmap;
  im: TImageControl;
  Color: TColor;
  Scanline: PAlphaColorArray;
  x,y,i: Integer;
begin
  for i:= 1 to 100 do begin
    im:= ImageControl1;
    Bitmap1:= TBitmap.Create(100,100);
    try
      for y:= 0 to 99 do begin
        ScanLine:= Bitmap1.ScanLine[y];
        for x:= 0 to 99 do begin
          ScanLine[x]:= Random(MaxInt);
        end;
      end;
      ImageControl1.Canvas.BeginScene;
      ImageControl1.Canvas.DrawBitmap(Bitmap1, RectF(0,0,Bitmap1.Width, Bitmap1.Height)
                                     ,im.ParentedRect,1,true);
      ImageControl1.Canvas.EndScene;
    finally
      Bitmap1.Free;
    end;
  end;
end;

Is there a faster way to draw pixels in Firemonkey?
I aim to make a demo program using Conway's game of life.

Johan
  • 74,508
  • 24
  • 191
  • 319

3 Answers3

6

All the time is spent performing these two lines of code:

ImageControl1.Canvas.BeginScene;
ImageControl1.Canvas.EndScene;

You can delete all the code that works with the bitmap and the code that actually draws the bitmap and it makes not one iota of difference to the runtime. In other words, the bottleneck is the scene code not the bitmap code. And I see no way for you to optimise that.

My test code looked like this:

Stopwatch := TStopwatch.StartNew;
for i:= 1 to 100 do begin
  ImageControl1.Canvas.BeginScene;
  ImageControl1.Canvas.EndScene;
end;
ShowMessage(IntToStr(Stopwatch.ElapsedMilliseconds));

This has the same elapsed time as your code, 1600ms on my machine. If you remove the BeginScene, DrawBitmap and EndScene calls then your code runs in 3ms on my machine.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • After testing it turns out that the `beginscene-endscene` pair is essential. If **that** takes up all the runtime, I'm stuck. I'll have a look at the sourcecode for begin- and endscene and see what's eating up all that runtime, methinks there's some threading-protection code in there (critical section or whatnot) that's slowing things down. – Johan Jan 25 '12 at 20:21
  • A critical section won't be slowing you down in a single threaded app. 70fps does seem a bit on the low side for a draw nothing app. – David Heffernan Jan 26 '12 at 07:43
-2

Here is a faster way to do that :

     procedure TForm2.Button1Click(Sender: TObject);
     var i,j: integer;
     begin
       for i := 0 to 200 do
       for j := 0 to 200 do ImageControl1.Bitmap.ScanLine[i][j]:=Random(Maxlongint);
       ImageControl1.Bitmap.BitmapChanged;
     end;

Quick and Optimized !...

Johan
  • 74,508
  • 24
  • 191
  • 319
-2

You can optimize your code like this:

procedure TForm15.Button1Click(Sender: TObject);
var
  Bitmap1: TBitmap;
  im: TImageControl;
  Color: TColor;
  ScanLine: PAlphaColorArray;
  x,y,i: Integer;
begin
  Bitmap1:= TBitmap.Create(100,100);
  try
    for i:= 1 to 100 do begin
      im:= ImageControl1;
      Scanline := PAlphaColorArray(Bitmap1.StartLine);
      for x := 0 to Bitmap1.Width * Bitmap1.Height do
        ScanLine[x] := Random(MaxInt);
      ImageControl1.Canvas.BeginScene;
      ImageControl1.Canvas.DrawBitmap(Bitmap1, RectF(0,0,Bitmap1.Width, Bitmap1.Height)
                                     ,im.ParentedRect,1,true);
      ImageControl1.Canvas.EndScene;
   end;
 finally
   Bitmap1.Free;
 end;
end;

Removed:

  • calling try finally in loop

  • creating TBitmap in loop

  • calling TBitmap.ScanLine method

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
PS.
  • 1
  • 1
  • 1
    I fixed some obvious errors, but the canvas doesn't get updated each time through `i`. Do we not have to worry about padding on FireMonkey bitmaps? You could also take `im:= ImageControl1;` out of the loop. Try adding `Bitmap1.UpdateHandles;` to update bitmap. – Marcus Adams Jan 24 '12 at 20:34
  • thank's, I don't have XE2 now. Next option is try to put `TImage` on form and `Image1.Bitmap := Bitmap1;` for buffer copy operation. – PS. Jan 24 '12 at 21:08
  • 1
    That takes exactly the same amount of time but doesn't actually work. It doesn't do the same as the code in the question. – David Heffernan Jan 24 '12 at 21:56
  • You cannot remove TBitmap.Scanline from the origianl code sample otherwise the bitmap is not modified at all – az01 Jan 25 '12 at 09:24
  • Thanks for trying, but your code does not actually work as expected, instead of displaying 100x it just displays 1x, for some reason if you do not recreate the bitmap every time (or use UpdateHandles) it does not recognize the change and does not display anything new. On top of that it takes essentially the same amount of CPU-time. (See David's answer for the reason why). – Johan Jan 25 '12 at 20:33
  • @MarcusAdams, You're not supposed to take anything out of the `for i` loop, the for i loop is meant to slow things down, so that it can be timed. `Bitmap1.UpdateHandles` works like a charm. – Johan Jan 25 '12 at 20:38