Clear a TBitmap before changing it in Delphi (VCL/Windows)

1.8k views Asked by At

This is about Delphi, 32 bits and Windows/VCL:

For a procedure that takes an input bitmap and writes to an output bitmap with a given format (PixelFormat: pf8Bit with a gray scale palette) I use the following code:

procedure ConvertBitmap(_inBmp: TBitmap; _outBmp: TBitmap);
begin
  _outBmp.PixelFormat := pf8Bit;
  _outBmp.Palette := MakeGrayPalette();
  _outBmp.SetSize(_inBmp.Width, _inBmp.Height);
  // code that works on ScanLines goes here
end;

The Output bitmap might not be empty when it is being passed to this procedure, but might contain anything in any format. It will be completely overwritten in the procedure.

I wonder whether simply changing its PixelFormat might create some overhead (e.g. convert the old content to the new pixel format), so should I somehow clear it first, e.g. by calling _outBmp.SetSize(0, 0) or any other means? If yes, what would be the most efficient way?

EDIT:

I just did some timing with this code:

procedure TimingTest;
var
  Stopwatch1: TStopwatch;
  Stopwatch2: TStopwatch;
  bmp: TBitmap;
  i: Integer;
begin
  bmp := TBitmap.Create;
  try
    Stopwatch1 := TStopwatch.Create;
    Stopwatch2 := TStopwatch.Create;
    for i := 0 to 10 - 1 do begin
      bmp.PixelFormat := pf24bit;
      bmp.SetSize(16 * 1024, 8 * 1024);
      Stopwatch1.Start;
      bmp.PixelFormat := pf8bit;
      bmp.Palette := MakeGrayPalette();
      Stopwatch1.Stop;
    end;
    for i := 0 to 10 - 1 do begin
      bmp.PixelFormat := pf24bit;
      bmp.SetSize(16 * 1024, 8 * 1024);
      Stopwatch2.Start;
      bmp.SetSize(0, 0);
      bmp.PixelFormat := pf8bit;
      bmp.Palette := MakeGrayPalette();
      Stopwatch2.Stop;
    end;
    l_Timing.Caption := Format('%.3f [s] vs %.3f [s]', [
    Stopwatch1.ElapsedMilliseconds / 1000, Stopwatch2.ElapsedMilliseconds / 1000]);
  finally
    FreeAndNil(bmp);
  end;
end;

The result on my computer (which is not slow): 5.331 [s] vs. 0.322 [s]

Unless I made an implementation error, this is quite significant. This is admittedly with a rather large bitmap though, but even with a more resonable size of 4096x4096 I get 0.693 vs. 0.040 which is still significant if this operation is executed many times (which it will be: several million times in fact).

This answers the question of whether there is a performance impact with a clear yes.

The remaining question is: Is SetSize(0,0) the most efficient way to clear a bitmap or is there a better way?

EDIT2:

TBitmap.Assign(nil) is another way to clear the bitmap:

    for i := 0 to 10 - 1 do begin
      bmp.PixelFormat := pf24bit;
      bmp.SetSize(16 * 1024, 8 * 1024);
      Stopwatch3.Start;
      bmp.Assign(nil);
      bmp.PixelFormat := pf8bit;
      bmp.Palette := MakeGrayPalette();
      Stopwatch3.Stop;
    end;

The timing is similar to .SetSize(0, 0).

1

There are 1 answers

0
Marco van de Voort On

I don't know the answer, but the test is flawed. The first set ends with a pf8bit correctly sized bitmap, the second is pf8bit 0x0. This is not the same. Somehow you don't allocate 10 times 16*1024 x 8 *1024 = 128MB bitmaps there.

The easiest option is of course to never change bittiness on existing bitmaps, but simply create a new one. With such large bitmaps, resizing and changing pixel type should be avoided at all costs if you care about performance.