天天看點

Delphi圖像處理 -- 圖像顯示

閱讀提示:

    《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圖像處理 -- 圖像顯示

    《Delphi圖像處理》系列使用GDI+單元下載下傳位址和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。

    因水準有限,錯誤在所難免,歡迎指正和指導。郵箱位址:[email protected]

    這裡可通路《Delphi圖像處理 -- 文章索引》。

繼續閱讀