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+位图
withImage1.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,
0
,
0
);
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
withImage1.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部分加入该单元,就可实现较多的图像格式选项。
unitGdipBitmap;
interface
uses
GdipTypes,Gdiplus,Windows,SysUtils,Classes,Graphics;
type
TGdipBitmap
=
class
(TBitmap)
public
procedureLoadFromStream(Stream:TStream);
override
;
procedureSaveToFile(
const
Filename:
string
);
override
;
procedureSaveToStream(Stream:TStream);
override
;
end;
implementation
{TGdipBitmap}
var
ImageFormat:
string
=
''
;
procedureSetImageFormat(
const
Filename:
string
);
begin
ImageFormat:
=
ExtractFileExt(Filename);
if
ImageFormat
<>
''
then
begin
Delete(ImageFormat,
1
,
1
);
if
CompareText(ImageFormat,
'
jpg
'
)
=
0
then
ImageFormat:
=
'
jpeg
'
else
if
CompareText(ImageFormat,
'
tif
'
)
=
0
then
ImageFormat:
=
'
tiff
'
;
end
else
ImageFormat:
=
'
bmp
'
;
ImageFormat:
=
'
image/
'
+
ImageFormat;
end;
procedureTGdipBitmap.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;
procedureTGdipBitmap.SaveToFile(
const
Filename:
string
);
begin
SetImageFormat(Filename);
inheritedSaveToFile(Filename);
ImageFormat:
=
''
;
end;
procedureTGdipBitmap.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
inheritedSaveToStream(Stream);
end;
initialization
//
TPicture.RegisterFileFormat('bmp','BMPFile',TGdipBitmap);
TPicture.RegisterFileFormat(
'
Exif
'
,
'
TIFFFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
tiff
'
,
'
TIFFFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
tif
'
,
'
TIFFFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
png
'
,
'
PNGFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
gif
'
,
'
GIFFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
jpeg
'
,
'
JPEGFile
'
,TGdipBitmap);
TPicture.RegisterFileFormat(
'
jpg
'
,
'
JPGFile
'
,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.PixelFormatof
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环境,一切正常:
usesGdiplus,GdipBitmap;
.....
......
procedureTForm1.Button5Click(Sender:TObject);
var
bmp:TGpBitmap;
g:TGpGraphics;
begin
if
OpenPictureDialog1.Executethen
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位图的相互转换。
如有错误或者建议、指导,请留言或者来信:[email protected]