有什么方法可以加速TPNGImage上的SaveToStream?

时间:2015-08-06 16:38:45

标签: delphi delphi-xe7

我有一个函数将TBitmap(我绘制)转换为TPngImage,然后将其保存到流中,因此其他方法可以使用它。使用Png是因为它为报告输出创建了较小的图像(excel,html)。问题是SaveToStream似乎花费了太多时间,比将TBitmap转换为TPngImage或使用带有png的TStream多15倍。这是代码:

var
 BitmapImage: TBitmap;      
 PNGImage: TPngImage;
 PngStream: TStream;        
begin
  // draw on BitmapImage
  ...
  PNGImage := TPngImage.Create;
  PNGStream := TMemoryStream.Create;
  Try
     PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG
     PNGImage.SaveToStream(PNGStream);  // Step 2: save PNG to stream
     WS.Shapes.AddPicture(PNGStream,PNGImage.Width,PNGImage.Height); // Step 3: Add PNG from Stream to Excel
  finally
     PNGImage.Free;
     PNGStream.Free;
  end;
...

这是用70000张图片测试的,时间如下:
第1步:7秒

第2步:93秒

第3步:6 s

为什么保存到Stream这么慢?有什么建议来优化这个吗?

使用Delphi XE7

修改

以下是带有简单bmp的示例(MCVE),它将转换为PNG然后保存到流中。只是为了另一个验证我添加了SaveToFile,当然需要更长的时间,但它保存到磁盘,所以我认为可以接受。

img1.bmp为49.5KB,保存的PNG为661字节。 链接到img1.bmp = http://www.filedropper.com/img1_1

TMemoryStreamAccess = class(TMemoryStream)
  end;

procedure TForm1.Button1Click(Sender: TObject);
var BitmapImage:TBitmap;
  PNGImage:TPngImage;
  PNGStream:TMemoryStream;//TStream;
  i,t1,t2,t3,t4,t5,t6: Integer;
  vFileName:string;
begin

  BitmapImage:=TBitmap.Create;
  BitmapImage.LoadFromFile('c:\tmp\img1.bmp');

  t1:=0; t2:=0; t3:=0; t4:=0; t5:=0; t6:=0;

  for i := 1 to 70000 do
  begin

    PNGImage:=TPngImage.Create;
    PNGStream:=TMemoryStream.Create;
    try

      t1:=GetTickCount;
      PNGImage.Assign(BitmapImage);
      t2:=t2+GetTickCount-t1;

      t3:=GetTickCount;
      TMemoryStreamAccess(PNGStream).Capacity := 1000;
      PNGImage.SaveToStream(PNGStream);
      // BitmapImage.SaveToStream(PNGStream); <-- very fast!
      t4:=t4+GetTickCount-t3;

    finally
      PNGImage.Free;
      PNGstream.Free
    end;

  end;

   showmessage('Assign = '+inttostr(t2)+' - SaveToStream = '+inttostr(t4));
end;

2 个答案:

答案 0 :(得分:6)

  

使用70000张图像进行测试,以下是时间:

     

步骤1:7 s

     

第2步:93秒

     

第3步:6 s

     

为什么保存到Stream这么慢?

让我们处理一些数字:

步骤1:7s = 7000ms。 7000/70000 =每张图像0.1ms

步骤2:93s = 93000ms。每张图像93000/70000 = ~1.33ms

步骤3:6s = 6000ms。 6000/70000 =每张图像~0.086ms

你认为每SaveToStream() 1.33毫秒是慢吗?你只是做了很多,所以他们随着时间的推移加起来,这就是全部。

话虽如此,内存中的PNG数据不会被压缩。保存数据时会压缩它。这是减速的一个原因。此外,保存PNG会对流执行大量写操作,这会导致流执行多个内存(重新)分配(TPNGImage还会在保存期间执行内部内存分配),这是另一个减速。

  

有什么建议可以优化这个吗?

您无法对压缩开销做任何事情,但在调用TMemoryStream.Capacity之前,您至少可以将SaveToStream()预先设置为合理的值,以减少TMemoryStream的内存重新分配需要在写作期间执行。你不需要与它完全一致。如果写入流导致其Size超过其当前Capacity,则会相应地增加其Capacity。由于您已经处理了70000张图片,请考虑它们的平均大小并为其添加更多KB,并将其用作初始Capacity

type
  TMemoryStreamAccess = class(TMemoryStream)
  end;

var
  BitmapImage: TBitmap;      
  PNGImage: TPngImage;
  PngStream: TMemoryStream;        
begin
  // draw on BitmapImage
  ...
  PNGImage := TPngImage.Create;
  Try
    PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG
    PNGStream := TMemoryStream.Create;
    try
      TMemoryStreamAccess(PNGStream).Capacity := ...; // some reasonable value
      PNGImage.SaveToStream(PNGStream);  // Step 2: save PNG to stream
      WS.Shapes.AddPicture(PNGStream, PNGImage.Width, PNGImage.Height); // Step 3: Add PNG from Stream to Excel
    finally
      PNGStream.Free;
    end;
  finally
    PNGImage.Free;
  end;
  ...

如果仍然不够快,请考虑使用线程并行处理多个图像。不要按顺序处理它们。

答案 1 :(得分:2)

您是否指定了压缩级别?我没注意到像

这样的东西
PNGImage.CompressionLevel := 1;
你的代码中的

。它可以在0到9的范围内。默认情况下,它是7.如果将其设置为1,它将显着更快,而输出流大小增加可以忽略不计。