閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元和《Delphi影像處理 -- 映像合成》中除例子外的全部代碼。
Delphi的TCanvas以及衍生類別提供了顯示TGraphic映像的方法Draw,其實這個方法本身並沒有顯示映像的功能,只是反過來調用了一下TGraphic的Draw方法。TGraphic本身是個抽象類別,其Draw方法也是個純虛方法,所以TGraphic的所有衍生類別必須提供一個具體的Draw方法。TGraphic的主要衍生類別TBitmap也有一個Draw方法,但是該方法只能利用其Transparent屬性顯示透明背景映像,而不能正確顯示帶Alpha通道的32位元影像像,即使Delphi2009以上版本的TGraphic增添了一個SupportsPartialTransparency屬性,但TBitmap也還是沒法直接顯示ARGB像素格式的映像,因為TBitmap調用的是Windows API的AlphaBlend函數,該函數似乎只能顯示PARGB格式像素的映像,而且TGraphic的SupportsPartialTransparency屬性還是唯讀性質的。
GDI+的TGpGraphics的系列DrawImage方法畫ARGB32位元影像像倒是很好的,但如果映像真的含Alpha資訊時,顯示的速度卻是較慢,大家可以用2張較大的圖片試一下,一張含Alpha,一張不含Alpha,對比一下就知道了。
因為本系列影像處理過程中有多個方法會使Alpha分量發生變化,即使該映像原來不含Alpha資訊,所以有必要寫映像顯示過程。而且,如果在應用程式中只是需要顯示處理過的映像,就不必再將TImageData類型轉換為TGraphic或者TGpBitmap了,直接使用本文的顯示過程無疑是很方便的。
本文的映像顯示是利用《Delphi影像處理 -- 映像合成》中的映像合成過程_DoMixer來完成的,因此,必須包括《Delphi影像處理 -- 映像合成》中除例子外的全部代碼:
function GetBitmapInfoHeader(const Data: TImageData): TBitmapInfoHeader;begin Result.biSize := Sizeof(TBitmapInfoHeader); Result.biWidth := Data.Width; Result.biHeight := Data.Height; Result.biPlanes := 1; Result.biBitCount := (Data.PixelFormat shr 8) and $ff; Result.biCompression := BI_RGB;end;procedure GetDCImageData(DC: HDC; x, y: Integer; var Data: TImageData; pbi: TBitmapInfo);var saveBitmap, Bitmap: HBITMAP; memDC: HDC;begin Bitmap := CreateCompatibleBitmap(DC, Data.Width, Data.Height); try memDC := CreateCompatibleDC(DC); saveBitmap := SelectObject(memDC, Bitmap); try BitBlt(memDC, 0, 0, Data.Width, Data.Height, DC, x, y, SRCCOPY); finally SelectObject(memDC, saveBitmap); DeleteDC(memDC); end; GetDIBits(DC, bitmap, 0, Data.Height, Data.Scan0, pbi, DIB_RGB_COLORS); finally DeleteObject(Bitmap); end;end;procedure BitBltImageData(DC: HDC; x, y: Integer; const Data: TImageData; pbi: TBitmapInfo);var saveBitmap, Bitmap: HBITMAP; memDC: HDC;begin Bitmap := CreateDIBItmap(DC, pbi.bmiHeader, CBM_INIT, Data.Scan0, pbi, DIB_RGB_COLORS); memDC := CreateCompatibleDC(DC); saveBitmap := SelectObject(memDC, Bitmap); try BitBlt(DC, x, y, Data.Width, Data.Height, memDC, 0, 0, SRCCOPY); finally SelectObject(memDC, saveBitmap); DeleteDC(memDC); DeleteObject(Bitmap); end;end;procedure ImageDraw(DC: HDC; x, y: Integer; const Data: TImageData; Alpha: Single = 1);var pbi: TBitmapInfo; dstR, srcR: TRect; alphaI: Integer; dst, src: TImageData; pData: PImageData;begin if GetClipBox(DC, dstR) <= NULLREGION then Exit; alphaI := Round(Alpha * 256); if alphaI < 0 then Exit; if alphaI > 256 then alphaI := 256; if (alphaI = 256) and not Data.AlphaFlag and ((Data.Width shl 2) = -Data.Stride) then begin pbi.bmiHeader := GetBitmapInfoHeader(Data); pData := @Data; _InvertScan0(pData^); BitBltImageData(DC, x, y, Data, pbi); _InvertScan0(pData^); Exit; end; dstR.Left := 0; dstR.Top := 0; srcR := Rect(x, y, Data.Width + x, Data.Height + y); if not IntersectRect(dstR, dstR, srcR) then Exit; if x < 0 then x := -x else x := 0; if y < 0 then y := -y else y := 0; src := GetSubImageData(Data, x, y, dstR.Right - dstR.Left, dstR.Bottom - dstR.Top); dst := NewImageData(src.Width, src.Height); dst.AlphaFlag := False; pbi.bmiHeader := GetBitmapInfoHeader(dst); if (alphaI < 256) or Data.AlphaFlag then GetDCImageData(DC, dstR.Left, dstR.Top, dst, pbi); _InvertScan0(dst); _DoMixer(dst, src, alphaI); _InvertScan0(dst); BitBltImageData(DC, dstR.Left, dstR.Top, dst, pbi); FreeImageData(dst);end;procedure TForm1.Button3Click(Sender: TObject);var source: TGpBitmap; dest: TBitmap; tmp: TJpegImage; src, dst: TImageData;begin dest := TBitmap.Create; tmp := TJpegImage.Create; tmp.LoadFromFile('..\..\media\IMG_9440_mf.jpg'); dest.Assign(tmp); tmp.Free; source := TGpBitmap.Create('..\..\media\xmas_011.png'); dst := GetBitmapData(dest); src := LockGpBitmap(source); ImageDraw(Canvas.Handle, 0, 0, dst); ImageDraw(Canvas.Handle, dst.Width, 0, src); ImageMixer(dst, src, 1); UnlockGpBitmap(source, src); ImageDraw(Canvas.Handle, dst.Width + src.Width, 0, dst); dest.Free; source.Free;end;
ImageDraw過程比GDI+的TGpGraphics.DrawImage方法要快,特別是顯示png之類的圖片。
本文介紹的只是最基本的顯示過程,還可以有展開形式和幾何變換形式的映像顯示方式,參照《Delphi影像處理 -- 幾何變換(上)》和《Delphi影像處理 -- 幾何變換(下)》代碼可以很容易完成。
上面代碼中例子的顯示效果同《Delphi影像處理 -- 映像合成》的第二副:
《Delphi影像處理》系列使用GDI+單元和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。