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, 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
  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'
    else if 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位图的相互转换。

GDI+图像与GDI位图的相互转换的更多相关文章

  1. GDI+ Bitmap与WPF BitmapImage的相互转换

    原文:GDI+ Bitmap与WPF BitmapImage的相互转换 using System.Windows.Interop; //... // Convert BitmapImage to Bi ...

  2. Delphi GDI对象之绘制位图

    http://www.cnblogs.com/pchmonster/archive/2012/07/06/2579334.html 绘制位图(Drawing Bitmaps) 绘制位图听起来似乎很难, ...

  3. GDI+图像编程

    一.Graphics GDI+是GDI(Windows Graphics Device Interface)的后继者,它是.NET Framework为操作图形提供的应用程序编程接口,主要用在窗体上绘 ...

  4. VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)

    上一节中鸡啄米讲了CDC类及其屏幕绘图函数,本节的主要内容是GDI对象之画笔CPen. GDI对象 在MFC中,CGdiObject类是GDI对象的基类,通过查阅MSDN我们可以看到,CGdiObje ...

  5. VS2010/MFC编程入门之五十一(图形图像:GDI对象之画刷CBrush)

    上一节中鸡啄米主要讲的是画笔CPen的用法,前面也说了,GDI对象中最常用的就是画笔和画刷,本节就讲讲画刷CBrush. 鸡啄米依然是通过实例的方式来说明画刷的用法.此实例要实现的功能是,对话框上有一 ...

  6. VS2010-MFC(图形图像:GDI对象之画刷CBrush)

    转自:http://www.jizhuomi.com/software/248.html 上一节讲的是画笔CPen的用法,前面也说了,GDI对象中最常用的就是画笔和画刷,本节就讲讲画刷CBrush. ...

  7. VS2010-MFC(图形图像:GDI对象之画笔CPen)

    转自:http://www.jizhuomi.com/software/246.html 上一节讲了CDC类及其屏幕绘图函数,本节的主要内容是GDI对象之画笔CPen. GDI对象 在MFC中,CGd ...

  8. GDI+学习---2.GDI+编程模式及组成类

    在使用GDI+的时候,您不必像在GDI中那样关心设备场景句柄,只需简单地创建一个Graphics对象,然后以您熟悉的面向对象的方式(如myGraphicsObject.DrawLine(paramet ...

  9. 超全面的.NET GDI+图形图像编程教程

    本篇主题内容是.NET GDI+图形图像编程系列的教程,不要被这个滚动条吓到,为了查找方便,我没有分开写,上面加了目录了,而且很多都是源码和图片~ (*^_^*) 本人也为了学习深刻,另一方面也是为了 ...

随机推荐

  1. JavaWeb开发之二《JSP + Tomcat的第一个程序“Hello World”》

    搬以前写的博客[2014-12-10 22:41] 前一篇博客讲了Tomcat环境搭建,于是开始第一个“Hello JSP”程序. 服务器环境搭建好之后,我们开始写自己的网站,这里简单的写一个没有后台 ...

  2. Map集合类(二.其他map集合jdk1.8)

    java集合笔记一 java集合笔记二 java集合笔记三 1.hashtable(线程安全) 1.存储数据为数组+链表2.存储键值对或获取时通过hash值取模数组长度确定节点在数组中的下标位置 in ...

  3. 关键字this super final static

    this关键字 1.this代表通过当前类实例化成的对象. 2.通过this来获取当前类的属性和方法 3.通过this和区分成员变量和局部变量 4.通过this(参数),调用当前类的构造方法 注意:通 ...

  4. Delphi 类(TObject、TPersistent、TComponent、TControl、TWinControl、TCustomControl、TGraphicControl、TInterfacedObject)简单介绍

      TObject:    VCL中所有类的根类,即是说:VCL中所有的类/组件/控件都是从TObject中继承而来.TObject类中定义了基本的 构造方法和析构方法.   TPersistent: ...

  5. AJAX(包括跨域)post请求封装

    function ajaxPost(dataUrl, parameter, callback, bef_callback, com_callback, err_callback) { $.ajax({ ...

  6. BZOJ 3159: 决战 解题报告

    BZOJ 3159: 决战 1 sec 512MB 题意: 给你一颗\(n\)个点,初始点权为\(0\)的有跟树,要求支持 Increase x y w 将路径\(x\)到\(y\)所有点点权加上\( ...

  7. Java桌球小游戏1

    版本三.使小球动起来package cn.xjion.game;/** * 水平滚动 * @author xjion * */import java.awt.*;import javax.swing. ...

  8. 4.2 react patterns(转)

    修改 Props Immutable data representation 确定性 在 getInitialState 中使用 props 私有状态和全局事件 render 包含 side effe ...

  9. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de 错误解决

    这是我们开启了bin-log, 我们就必须指定我们的函数是否是1 DETERMINISTIC 不确定的2 NO SQL 没有SQl语句,当然也不会修改数据3 READS SQL DATA 只是读取数据 ...

  10. could not stop cortex-m device

    检查一下STM32复位管脚是不是0V,如果是0V的话并且你有上拉电阻,那么就断电后检查一下STM32的VCC和GND是否短路,我的就是两个贴片电容击穿造成的短路从而使RST无法拉高.