Delphi 7中對StretchBlt, StretchDIBits, DrawDibDraw, BitBlt 的性能測試 - 原創


我的天哪,上一篇博文是2年前的事情了。看來又虛度了2年光陰,繼續學習。。。
本文算是副產品,正品是利用 FFmpeg 從任意視頻中生成 GIF 片段的小程序,等寫完了再發。不為別的,只是為了給兒子做動圖,且看不慣這種工具也要收費!

V2G 正品已出爐,雖然不大像樣,但好歹是能用,請見:用 Delphi 7 實現基於 FFMS2 的視頻轉 GIF 工具

聲明

本文是首先看到了求比 Stretchblt 方法更快的縮放算法的帖子,請參看其中署名為“張輝明”的回復。我做了優化和一些修正,但 DrawDibDraw 部分的調用是原文照錄的。(其實上文就是我 Bing 了 DrawDibDraw 時搜到的。)

為什么要測試 StretchBlt、StretchDIBits、DrawDibDraw 的性能

因為視頻回放需要很高的顯示性能,解碼占了很多計算量,留給顯示的時間不多,能優化則優化吧。

其實現在的 CPU 跑個視頻播放已經綽綽有余了,GPU 壓根就不必用。即便是用 Delphi 自帶的 TImage 控件,用 Bitmap 往里填也可以滿足普通播放需求了。如果時光倒流到 10 年前,那可真是得去研究 DirectX、OpenGL了。可惜關於這哥倆,大部分都是 C、C++ 的資源,我啃了半天 SDL,覺得有點殺雞用牛刀。所以就想着先實現需求吧,真的不行了再優化吧。在我的 Intel i3 3220 上,用 StretchDIBits 播放視頻時最多也就跑了 22%。

為什么還抱着 Delphi 不放?

  1. 性價比第一
    敢問性能、便捷、體積俱佳的 Windows 開發環境,誰敢和 Delphi 比?C#,Java 是優秀,可為了一個小功能就跑它個虛擬機,實在划不來啊。C++ 倒是夠 sharp,可學習過程太痛苦了,代碼還不容易寫。
  2. 全能
    都說 Python 好,可我眼拙,實在看不出來好在哪里,局限性太大。唯一的好處是能讓新手快速上手編程,還有一個好處是能讓你忘記計算機是怎么運作的!
  3. 懷舊
    十幾年前自學的東西,從 Delphi 3 開始用,有感情了。只要 Windows 不停止對 32 位程序的支持,我就會一直用下去。(關於這一點,我要狠狠鄙視 Apple 一下。)
  4. Delphi 7是經典
    和 Visual Studio、水果一樣,當年 Borland 的產品也有大小年,逢單的版本就是穩定一些。雖然輪子有時候得從頭開始造,但是“知其所以然”是樂在其中的事,相信我!

測試結果

如果只關心結果,或者對 Delphi 不屑,那您就不必往下看了,我先給出結果吧。為您節省點時間。嚴格意義上說,BitBlt不屬於其他哥仨的陣營,因為不用縮放,所以速度當然快了。放在這里比較,就當是個 Baseline 吧。

  1. DrawDibDraw 最快(1ms 級別)。
    不到 StretchBlt和StretchDIBits 的一半,且不需要用 SetStretchBltMode 設置什么縮放模式,畫質看不出分別。
  2. StretchBltStretchDIBits 難分伯仲。
    用了色彩擬合模式(HALFTONE)的話會大大增加計算量,耗時4倍,比 DrawDibDraw 慢1個數量級。建議縮小圖像時可以用 COLORONCOLOR 模式,肉眼看不出區別,但可以比 HALFTONE 模式提速4倍!
API COLORONCOLOR HALFTONE
BitBlt 400 400
DrawDibDraw 1125 1125
StretchBlt 3000 11406
StretchDIBits 3203 11576
  • 測試用機:CPU: Intel i3 3220,內存: 8G DDRIII 1333,顯卡: AMD Radeon HD 7700 (對測試結果沒影響吧),Windows 10專業版
  • 測試次數:1000次
  • 時間單位:millisecond(毫秒)
  • COLORONCOLOR:刪除不需要的點。
    這是 SetStretchBltMode 的參數,指定目標設備(區域)的縮放模式。在用 StretchDIBits 和 StretchBlt 時必須得設置一個縮放模式,不然,嘿嘿,慘不忍睹。官方說明是:“Deletes the pixels. This mode deletes all eliminated lines of pixels without trying to preserve their information.”,中文意思大概就是:刪除不需要的像素點。該模式刪除所有無用的點陣,這些點的所有信息都不予保留。 參見 SetStretchBltMode
  • HALFTONE:將源區域的顏色溶入目標區域中去。
    作用同上。官方說明是:“Maps pixels from the source rectangle into blocks of pixels in the destination rectangle. The average color over the destination block of pixels approximates the color of the source pixels.”中文大概意思是:將源矩形區域的像素點信息擬合到目標區域周邊的多個像素塊中。目標區域多個像素塊的顏色值會進行平均,以便最大程度地接近源像素的色彩。參見 SetStretchBltMode

源碼

界面

就放了幾個按鈕而已,名稱末尾為C的表示用了 COLORONCOLOR 模式,為H的表示用了 HALFTONE 模式。還有一個 Timage 控件。

常量

FileName 定義了 Bmp 圖片文件名,Count 定義了測試循環的次數。

FileName='1.bmp';
Count=1000;
FontSize=20;

BMP 文件讀取

因為 StretchBlt和BitBlt 只需要提供源 HDC,不需要用 tagBITMAPINFO 和原始 RGB 數據區作為參數,所以直接用了 TBitmap 控件載入圖片文件。

procedure TMainForm.StretchBltDisplay;
var
  bmp : TBitmap ;
  i : Integer ;
  Start : DWORD ;
begin
  Bmp:= TBitmap.Create ;
  bmp.LoadFromFile(FileName);

  Start := GetTickCount ;
  for i := 1 to count do
  begin
    StretchBlt(image1.Canvas.Handle, 0, 0, image1.ClientWidth, image1.ClientHeight,
              bmp.Canvas.Handle, 0,0,bmp.Width,bmp.Height, SRCCOPY);
    image1.Canvas.TextOut(10,10,inttostr(i));
    image1.Refresh;
  end;
  MainForm.Caption := IntToStr(GetTickCount - Start);

  bmp.Free ;
end;

DrawDibDraw和DrawDibDraw都需要用到BMP原始信息做參數,所以只好寫了個LoadBmp從文件中讀取數據。
因為要把原始信息帶出去,所以帶了var前綴。

procedure LoadBmp(bmpFile: String; var bmpinfo:TBitmapInfo; var pBmpData:Pointer);
var
  bmf: TBitmapFileHeader;
  imageSize: LongWord;
  Stream: TFileStream;
begin
   try
    Stream:= TFileStream.Create(bmpFile, fmOpenRead or fmShareDenyWrite);
    Stream.Read(bmf, sizeof(Bmf));
    Stream.Read(bmpinfo, sizeof(bmpinfo));
    imageSize:= bmf.bfSize-bmf.bfOffBits;
    stream.Seek(bmf.bfOffBits,0);

    FreeMem(pBmpData);
    GetMem(pBmpData, imageSize);

    Stream.Read(pBmpData^, ImageSize);
  finally
    FreeAndNil(Stream);
  end;
end;

關於 var 前綴

一開始以為,用指針就可以在函數內給外部的指針分配內存並傳出結果了。但其實不對,外面的指針還一直是 nil。必須帶上 var 前綴才行(指針的指針)。

關於 VFW

DrawDibDraw 是 VFW(Video for Windows)中的 API,關於 DrawDibDraw 的用法可以參考園子里的 DrawDibDraw函數的使用方法。封裝文件 VFW.pas 來自一篇《delphi 攝像頭編程 vfw》,出處已不可考,被署名 Tom Nuydens 的修改過。

完整源碼

結論和建議

  • 單純縮小畫面的(源圖一定比目標圖大):StretchBlt、StretchDIBits 隨便用,先用 SetStretchBltMode 選 COLORONCOLOR 模式,性能足夠了。
  • 必須放大畫面的(源圖比目標圖小):要用StretchBlt、StretchDIBits,用SetStretchBltMode必須選HALFTONE模式。性能無法接受可選 DrawDibDraw。
  • 圖省事用 DrawDibDraw,可能要多耗些資源吧(沒精確測算過)。
  • 圖形性能要求更高的,啃DirectX、OpenGL、SDL去吧。代碼不難,難的是要理解那么多圖形學概念。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM