【问题标题】:Clear a TBitmap before changing it in Delphi (VCL/Windows)在 Delphi (VCL/Windows) 中更改之前清除 TBitmap
【发布时间】:2020-04-21 01:17:58
【问题描述】:

这是关于 Delphi、32 位和 Windows/VCL:

对于接受输入位图并以给定格式(PixelFormat:pf8Bit 带灰度调色板)写入输出位图的过程,我使用以下代码:

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;

输出位图在传递给此过程时可能不为空,但可能包含任何格式的任何内容。它将在程序中被完全覆盖。

我想知道简单地更改它的 PixelFormat 是否会产生一些开销(例如,将旧内容转换为新的像素格式),所以我应该先以某种方式清除它,例如通过调用 _outBmp.SetSize(0, 0) 或任何其他方式?如果是,最有效的方法是什么?

编辑:

我只是用这段代码做了一些计时:

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;

我电脑上的结果(不慢): 5.331 [s] 与 0.322 [s]

除非我犯了实施错误,否则这非常重要。诚然,这是一个相当大的位图,但即使使用更合理的 4096x4096 尺寸,我也会得到 0.693 与 0.040,如果多次执行此操作(实际上将是:几百万次),这仍然很重要。

这回答了是否对性能有影响的问题,答案是肯定的。

剩下的问题是:SetSize(0,0) 是清除位图最有效的方法还是有更好的方法?

EDIT2:

TBitmap.Assign(nil) 是另一种清除位图的方法:

    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;

时序类似于.SetSize(0, 0)。

【问题讨论】:

  • 您对目前的代码有实际问题吗?
  • 这不是问题,但是总的代码(包括隐藏在 // ScanLines 上的代码)可能需要一些性能改进。到目前为止,我已经减少了对 TBitmap.ScanLine[] 的调用次数,这很有帮助,并将 PixelFormat 从 pf24Bit 减少到 pf8Bit,这也得到了一些微秒。现在我正在研究其他领域,这让我觉得可能会提高性能。 (在你问之前:不,我还没有做任何时间安排,但我正在这样做。)
  • 当您不知道这是否有任何好处时,询问如何清除位图(无论这意味着什么)是没有意义的。您应该尝试根据可衡量的数据优化您的代码。
  • 0.324s 与 0.322s 相同,完全在可变性范围内。我认为Assign(nil) 是清除实例的规范方式。但是,我可能会选择创建一个新的位图实例。如果您关心性能,那么您可能会考虑使用 graphics32 之类的库。
  • 我猜你对 TBitmap.Create 的成本有误。在看到毫秒范围内的时间之前,我需要将循环增加到 100。即使是 Free/Create 也与将大小设置为 0 一样快。

标签: delphi vcl delphi-2007 tbitmap


【解决方案1】:

我不知道答案,但测试有缺陷。第一组以 pf8bit 大小正确的位图结束,第二组是 pf8bit 0x0。这不一样。不知何故,您不会在那里分配 10 次 16*1024 x 8 *1024 = 128MB 位图。

最简单的选择当然是永远不要更改现有位图上的位图,而只需创建一个新位图。对于如此大的位图,如果您关心性能,则应不惜一切代价避免调整大小和更改像素类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 2018-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多