1. 程式人生 > >GDI+ 在Delphi程式的應用 -- GDI+影象與GDI點陣圖的相互轉換

GDI+ 在Delphi程式的應用 -- GDI+影象與GDI點陣圖的相互轉換

        Delphi的TBitmap封裝了Windows的GDI點陣圖,因此,TBitmap只支援bmp格式的影象,但是在Delphi應用程式中,常常會遇到圖形格式的轉換,如將Delphi點陣圖TBitmap的影象轉換為其它格式儲存,或者將其它影象格式轉換為TBitmap等。這時候,我們往往藉助一些第三方元件或程式碼,Delphi自帶的TJPEG.pas就是jpeg格式影象轉換的第三方程式碼單元。

        其實,利用GDI+的TGpBitmap可以很方便的和TBitmap實現一些影象格式的相互轉換,下面的程式碼把一個PNG格式影象轉換為Delphi視窗介面上的TImage影象進行顯示:

var
  bmp: TGpBitmap;
begin
//  bmp := TGpBitmap.Create('D:del_gdiplusDemosMediamsn1.gif');
//  bmp := TGpBitmap.Create('D:del_gdiplusDemosMediaMultiFrame.tif');  bmp := TGpBitmap.Create('D:del_gdiplusDemosMediaclimber.png');  Image1.Picture.Bitmap.Handle := bmp.GetHBITMAP(0);
  bmp.Free;
end;

        程式碼中首先用GDI+的TGpBitmap從磁碟檔案裝入png格式影象,然後直接取GDI+點陣圖的影象控制代碼賦給TBitmap的Handle屬性,完成轉換。程式碼中的TGpBitmap.GetHBITMAP方法中有個影象轉換時背景顏色引數,對於非透明影象,這個引數是忽略的,由於Delphi的TBitmap不支援透明影象,所以,即使是有透明部分的PNG影象,該引數也不起什麼作用,無論你設定什麼顏色,透明背景總是黑色。只要是GDI+支援的影象格式,都可以很方便的轉換為TBitmap,程式碼中被註釋的程式碼分別為GIF和TIFF影象格式的轉換。

        同樣,我們也可以把TBitmap的影象利用GDI+轉換為GDI+所支援的格式:

var
  bmp: TGpBitmap;
  g: TGpGraphics;
  Clsid: TGUID;
begin
  
// 利用TImage.Picture.Bitmap的影象控制代碼Handle和調色盤屬性Palette建立一個GDI+點陣圖  with Image1.Picture.Bitmap do
  bmp :
= TGpBitmap.Create(Handle, Palette);
  
// 轉換為PNG格式儲存if GetEncoderClsid('image/png', Clsid) then
    bmp.Save(
'd:gdi_test.png', Clsid);
  
//  顯示在視窗  g := TGpGraphics.Create(Handle, False);
  g.DrawImage(bmp, 
00);
  bmp.Free;
  g.Free;
end;

        上面例子把TImage控制元件的影象轉換成了png格式影象,注意,如果是第三方程式碼轉換裝入的TImage影象,有可能不支援上面的轉換,如利用TJPEGImage裝入的影象,因TJPEGImage是從TGraphic繼承而來的,沒有提供HBITMAP型別的Handle屬性,所以轉換不會成功。

        也可利用GDI+對TBitmap點陣圖進行壓縮儲存,下面的例子把TImage的影象壓縮50%儲存為jpeg格式影象:

var
  bmp: TGpBitmap;
  g: TGpGraphics;
  Clsid: TGUID;
  Parameters: TEncoderParameters;
  Quality: Integer;
  GUID: TGUID;
begin
  with Image1.Picture.Bitmap 
do
  bmp :
= TGpBitmap.Create(Handle, Palette);
  
if GetEncoderClsid('image/jpeg', Clsid) then
  begin
    Parameters.Count :
=1;
    Parameters.Parameter[
0].Guid := EncoderQuality;
    Parameters.Parameter[
0].ValueType := EncoderParameterValueTypeLong;
    Parameters.Parameter[
0].NumberOfValues :=1;
    Quality :
=50;                             // 圖片質量50    Parameters.Parameter[0].Value := @Quality;
    bmp.Save(
'd:gdi_test.jpg', Clsid, @Parameters);
  end;
  g :
= TGpGraphics.Create(Handle, False);
  g.DrawImage(bmp, 
00);
  bmp.Free;
  g.Free;
end;

        目前的GDI+版本只支援jpeg影象格式的壓縮,例子中的影象編碼器壓縮引數設定可以參見我的文章《GDI+ 在Delphi程式的應用 -- 多幀(頁)影象的分解與合成》,裡面有較詳細的解說。

        在Delphi程式中,可以用TOpenPictureDialog和TSavePictureDialog對話方塊實現影象的存取,同樣,沒有第三方程式碼支援,也只有有限幾種影象格式供選擇,我們可以利用GDI+寫個簡單的轉換程式碼單元,在程式程式碼單元的uses部分加入該單元,就可實現較多的影象格式選項。

unit GdipBitmap;

interface

uses
  GdipTypes, Gdiplus, Windows, SysUtils, Classes, Graphics;

type
  TGdipBitmap 
=class(TBitmap)
  
public
    procedure LoadFromStream(Stream: TStream); 
override;
    procedure SaveToFile(
const Filename: string); override;
    procedure SaveToStream(Stream: TStream); 
override;
  end;

implementation

{ TGdipBitmap }

var
  ImageFormat: 
string='';

procedure SetImageFormat(
const Filename: string);
begin
  ImageFormat :
= ExtractFileExt(Filename);
  
if ImageFormat <>'' then
  begin
    Delete(ImageFormat, 
11);
    
if CompareText(ImageFormat, 'jpg'=0 then
      ImageFormat :
='jpeg'elseif CompareText(ImageFormat, 'tif'=0 then
      ImageFormat :
='tiff';
  end 
else ImageFormat :='bmp';
  ImageFormat :
='image/'+ ImageFormat;
end;

procedure TGdipBitmap.LoadFromStream(Stream: TStream);
var
  Adaper: TStreamAdapter;
  tmp: TGpBitmap;
begin
  Adaper :
= TStreamAdapter.Create(Stream, soReference);
  tmp :
= TGpBitmap.Create(Adaper);
  
try
    Handle :
= tmp.GetHBITMAP(0);
  
finally
    tmp.Free;
  end;
end;

procedure TGdipBitmap.SaveToFile(
const Filename: string);
begin
  SetImageFormat(Filename);
  inherited SaveToFile(Filename);
  ImageFormat :
='';
end;

procedure TGdipBitmap.SaveToStream(Stream: TStream);
var
  tmp: TGpBitmap;
  Adaper: TStreamAdapter;
  Clsid: TGUID;
begin
  
if (ImageFormat <>'') and (GetEncoderClsid(ImageFormat, Clsid)) then
  begin
    tmp :
= TGpBitmap.Create(Handle, Palette);
    
try
      Adaper :
= TStreamAdapter.Create(Stream, soReference);
      tmp.Save(Adaper, Clsid);
    
finally
      tmp.Free;
    end;
  end 
else
    inherited SaveToStream(Stream);
end;

initialization
//  TPicture.RegisterFileFormat('bmp', 'BMP File', TGdipBitmap);  TPicture.RegisterFileFormat('Exif''TIFF File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'tiff''TIFF File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'tif''TIFF File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'png''PNG File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'gif''GIF File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'jpeg''JPEG File', TGdipBitmap);
  TPicture.RegisterFileFormat(
'jpg''JPG File', TGdipBitmap);
finalization
  TPicture.UnregisterGraphicClass(TGdipBitmap);
end.

        上面就是我寫的一個簡單GDI+影象轉換單元,在單元的initialization部分,向Delphi註冊了幾種影象格式的存取類TGdipBitmap,其中bmp格式註冊程式碼被登出了,還是用預設的TBitmap開啟為好。程式碼中用的就是前面所說的轉換原理,不過,用這種方式轉換的TBitmap影象格式都是32位的,可以在TGdipBitmap.LoadFromStream方法的Handle := tmp.GetHBITMAP(0);語句後面加入程式碼進行影象畫素格式的轉換:

    Handle := tmp.GetHBITMAP(0);
    
case tmp.PixelFormat of
      pf1bppIndexed: PixelFormat :
= pf1bit;
      pf4bppIndexed: PixelFormat :
= pf4bit;
      pf8bppIndexed: PixelFormat :
= pf8bit;
      pf16bppRGB565, pf16bppRGB555, pf16bppARGB1555: PixelFormat :
= pf16bit;
      pf24bppRGB: PixelFormat :
= pf24bit;
      pf32bppRGB, pf32bppARGB: PixelFormat :
= pf32bit;
      
else PixelFormat := pfCustom;
    end;

        下面的程式碼演示了使用該單元后利用對話方塊開啟影象,應提醒的是,在Delphi的IDE除錯執行狀態下,當選擇png影象格式時,會彈出CPU除錯視窗,這不知是Gdiplus.dll的BUG,還是Delphi的問題但是不影響程式的正確執行,脫離IDE環境,一切正常:

uses Gdiplus, GdipBitmap;
.....
......
procedure TForm1.Button5Click(Sender: TObject);
var
  bmp: TGpBitmap;
  g: TGpGraphics;
begin
  
if OpenPictureDialog1.Execute then
  begin
    bmp :
= TGpBitmap.Create(OpenPictureDialog1.FileName);
    g :
= TGpGraphics.Create(Handle, False);
    g.DrawImage(bmp, 
00);
    bmp.Free;
    g.Free;
  end;
end;

        其實,如果你願意,可以把該單元通過Delphi的Component->Install Component選單,建立一個新的包或者把單元加到預設的包中,確定安裝後,可以在設計期直接用TImage的Picture屬性進行多種影象格式檔案的選擇。

        上面的例子程式碼中使用的GDI+單元是我自己改寫的,如果用其它版本的GDI+單元,應作適當的修改,我的GDI+單元下載地址可以在文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》中找到,並注意文章最後的幾處修改。

        順便說一句,即使不使用Delphi的朋友,也可用文章中的轉換方法,利用GDI的HBITMAP和HPALETTE,實現GDI+影象與GDI點陣圖的相互轉換。