Pixels プロパティはこんなに遅い

TBitmap のピクセルに気軽にアクセスする手段として TBitmap.Canvas.Pixels[X, Y: Integer]: TColor があります。
これがまた桁外れに遅いのです。
プロパティなので関数呼び出しとほぼ等価なコストがかかることと、ピクセルのビット数を気にしなくて済むように裏で作業しているということ。
この2つが主な原因だと思われます(調査したわけじゃないので、正しいかどうかの保証はできませんが)
それを回避する方法は Pixels プロパティを使わないことです(当たり前(笑))
どのように回避しているかは以下参照。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TRGBTripleArray = array[0 .. High(Integer) div 3 - 1] of RGBTRIPLE;
  PRGBTripleArray = ^TRGBTripleArray; //配列型のポインタを利用して配列的にアクセス
                                      //良くわからない人は次の tip を参照
                                      //(余計わからなくなっても責任は取りませんが(笑))
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Cardinal;
  m, n, k: Integer;
  C : RGBTRIPLE;
  Bitmap: TBitmap;
  P: PRGBTripleArray;
begin
  Bitmap := TBitmap.Create;
  Bitmap.PixelFormat := pf24bit;
  Bitmap.Width := 640;
  Bitmap.Height := 480;

  with C do
  begin
    rgbtBlue := 0;
    rgbtGreen := 0;
    rgbtRed := 0;
  end;

  //SLOW
  I := GetTickCount;
  for n := 0 to 480 - 1 do
    for m := 0 to 640 - 1 do
      Bitmap.Canvas.Pixels[m, n] := clBlack;
  Memo1.Lines.Add('SLOW: ' + IntToStr(GetTickCount - I));

  //FAST
  I := GetTickCount;
  for n := 0 to 480 - 1 do
    for m := 0 to 640 - 1 do
      PRGBTripleArray(Bitmap.ScanLine[n])[m] := C;
  Memo1.Lines.Add('FAST: ' + IntToStr(GetTickCount - I));

  //OPTIMIZED FAST
  I := GetTickCount;
  for k := 0 to 99 do  //GetTickCount の解像度以下になってしまうので複数回まわして計測。
    for n := 0 to 480 - 1 do
    begin
      P := Bitmap.ScanLine[n];  //シーケンシャルアクセスなら毎回する必要性は当然ない
      for m := 0 to 640 - 1 do
      begin
        P[m] := C;
      end;
    end;
  Memo1.Lines.Add('OPTIMIZED FAST: ' + IntToStr((GetTickCount - I) div 100));

  Bitmap.Free;
end;

end.
SLOW: 1685
FAST: 415
OPTIMIZED FAST: 8

PentiumU 266PE MHzで測定。

何気なく使っている GetTickCount は Windows 起動後の経過時間をミリ秒で返す Win32API です。
時間計測には便利な関数ですね。
MMSystem を uses して timeGetTime を使ったほうが精度は高いですが(苦笑)


Return index page