GDI+ 在Delphi程式的應用 -- GDI+影象與GDI點陣圖的相互轉換
Delphi的TBitmap封裝了Windows的GDI點陣圖,因此,TBitmap只支援bmp格式的影象,但是在Delphi應用程式中,常常會遇到圖形格式的轉換,如將Delphi點陣圖TBitmap的影象轉換為其它格式儲存,或者將其它影象格式轉換為TBitmap等。這時候,我們往往藉助一些第三方元件或程式碼,Delphi自帶的TJPEG.pas就是jpeg格式影象轉換的第三方程式碼單元。
其實,利用GDI+的TGpBitmap可以很方便的和TBitmap實現一些影象格式的相互轉換,下面的程式碼把一個PNG格式影象轉換為Delphi視窗介面上的TImage影象進行顯示:
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+所支援的格式:
varbmp: 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(
// 顯示在視窗 g := TGpGraphics.Create(Handle, False);
g.DrawImage(bmp, 0, 0);
bmp.Free;
g.Free;
end;
上面例子把TImage控制元件的影象轉換成了png格式影象,注意,如果是第三方程式碼轉換裝入的TImage影象,有可能不支援上面的轉換,如利用TJPEGImage裝入的影象,因TJPEGImage是從TGraphic繼承而來的,沒有提供HBITMAP型別的Handle屬性,所以轉換不會成功。
也可利用GDI+對TBitmap點陣圖進行壓縮儲存,下面的例子把TImage的影象壓縮50%儲存為jpeg格式影象:
varbmp: 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, 0, 0);
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, 1, 1);
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, 0, 0);
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點陣圖的相互轉換。