分享

用Delphi实现24位真彩色图标

 独孤求财 2012-03-12

用Delphi实现24位真彩色图标

ZDNet软件频道 时间:2007-10-31 作者:李金刚 | 计算机与信息技术 我要评论()
本文关键词:
本文详细介绍了ICO文件的格式,以及利用Jpeg、BMP等格式的24位真彩色图片,生成图标的一种方法
24位真彩色图形转化为ICO文件
摘要本文详细介绍了ICO文件的格式,以及利用Jpeg、BMP等格式的24位真彩色图片,生成图标的一种方法。

  关键词图标,位图,24位真彩色

  引言

  Delphi是目前广泛使用的可视化开发工具,它自身带有一个图片、图标的编辑器——ImageEditor,但是到Delphi7为止,都不能进行真彩图标的编辑,可以说是一个遗憾。笔者通过对图标文件的研究,实现了产生24位真彩色图标。

  图标文件的格式

  首先,分析一个具体的图标。在CS1.6中有一个图标game.ico(),如果用WinHex等可以进行16进制编辑的软件打开这个图标文件,我们可以看到如下数据:

00000100040010100000000000006805
00004600000010100000000000006803
0000AE0500002020000000000000A808
0000160900002020000000000000A80C
0000BE11000028000000100000002000
00000100080000000000400100004746
6C656D696E6700010000000000000000

  下面我们就说一说,这些数据的具体含义。一个图标文件(*.ICO),实际上可以含有多个图标.通常,每个图标都会被转换为针对特定显示设备的图标图像。图标文件由文件头和数据组成,ICO文件一开始,是一个叫做tagIconDir的记录型的结构,在Delphi中这样来描述(括号内的数值,是针对CS图标的具体数据):

tagIconDir=packedrecord
idReserved:WORD;//保留域,目前始终为0(开始的数据$0000)
idType:WORD;//定义为资源类型,图标值为$0001、光标是$0002($0001)
idCount:WORD;//idCount表示的是这个文件里包含了几个图标($0004)
idEntries:array[0..0]oftagIconDirEntry;//不包括本数组,以上一共6个字节
end;
  这个记录中的idEntries是个数组结构,这个结构的大小不是始终为1的一个数组,它需要根据图标数目(idCount)来确定真实的数组大小。它的类型为tagIconDirEntry记录,定义如下:

tagIconDirEntry=packedrecord
bWidth:BYTE;//图标图片的显示宽度,以像素为单位,最大值为255($10=16D)
bHeight:BYTE;//图标图片的显示高度,以像素为单位,最大值为255($10=16D)
bColorCount:BYTE;//图标图片的颜色数($00)
bReserved:BYTE;//保留域总是0($00)
wPlanes:WORD;//图标图片的位面数($0000)
wBitCount:WORD;//图标图片的颜色深度($0000)
dwBytesInRes:DWORD;//图标图片占用的数据量($00000568)
dwImageOffset:DWORD;//图标图片的开始位置($00000046)
end;.//这个结构是16个字节
  上面说的idCount表示图标文件里包含的图标个数,每个图标都要有一个tagIconDirEntry结构来表示图标的具体信息。根据本结构的dwBytesInRes和dwImageOffset我们就可以确定图片(图标)的位置了。在该位置的数据是一个称为agIconImage的记录,它是这样定义的:

tagIconImage=packedrecord
icHeader:TBitmapInfoHeader;//BMP文件的信息头
icColors:array[0..0]ofTRGBQuad;
icXOR:array[0..0]ofBYTE;
icAND:array[0..0]ofBYTE;
end;
  从这个定义中我们可以看出,这个内容就是一个标准的位图格式,只不过多了两项,icXOR和icAND,普通的位图信息里是没有这2个成员的。大家知道,图标在被显示时,是利用遮罩方法将2副位图在同一个位置显示才产生任意轮廓的,先使用XOR位图抠出需要显示的区域,然后再在抠出的区域中显示出需要显示的图形。由于这个缘故,图标的位图格式中的位图信息头(TBitmapInfoHeader)是2个位图共用的。它与普通位图头信息最大的不同是TBitmapInfoHeader.biHeight成员,显然它是2副位图高度的总和。讲到这里,我们需要对位图(BMP)文件的格式有些了解了。

  位图文件的格式

  BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。按照微软的定义,在开始的文件头由14个字节组成:

tagBITMAPFILEHEADER=packedrecord
bfType:WORD;//位图文件的类型,必须为BM
bfSize:DWORD;//位图文件的大小,以字节为单位
bfReserved1:WORD;//位图文件保留字,必须为0
bfReserved2:WORD;//位图文件保留字,必须为0
bfOffBits:DWORD;//位图数据的起始位置,以相对于位图
//文件头的偏移量表示,以字节为单位
End;
  紧接着上一记录的是位图信息头tagBITMAPINFOHEADER,BMP位图信息头数据用于说明位图的尺寸等信息。这个信息头就是上文说的TBitmapInfoHeader,它的长度固定为40字节。

tagBITMAPINFOHEADER=packedrecord
biSize:DWORD;//本结构所占用字节数
biWidth:LONGINT//位图的宽度,以像素为单位
biHeight;:LONGINT//位图的高度,以像素为单位
biPlanes;:WORD//目标设备的级别,必须为1
biBitCount:WORD//每个像素所需的位数,必须是1(双色),
//4(16色),8(256色)或24(真彩色)之一
biCompression:DWORD;//位图压缩类型,必须是0(不压缩),
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
biSizeImage:DWORD;//位图的大小,以字节为单位
biXPelsPerMeter:LONGINT;//位图水平分辨率,每米像素数
biYPelsPerMeter:LONGINT;//位图垂直分辨率,每米像素数
biClrUsed:DWORD;//位图实际使用的颜色表中的颜色数
biClrImportant:DWORD;//位图显示过程中重要的颜色数
End;
  紧接着就是颜色表,用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:

tagRGBQUAD=packedrecord
rgbBlue:BYTE;//蓝色的亮度(值范围为0-255)
rgbGreen:BYTE;//绿色的亮度(值范围为0-255)
rgbRed:BYTE;//红色的亮度(值范围为0-255)
rgbReserved:BYTE;//保留,必须为0
end;
  颜色表中RGBQUAD结构数据的个数有biBitCount来确定:

  当biBitCount=1,4,8时,分别有2,16,256个表项;

  当biBitCount=24时,没有颜色表项。

  位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:

tagBITMAPINFO=packedrecord
bmiHeader:BITMAPINFOHEADER;//位图信息头
bmiColors[0..0]:RGBQUAD;//颜色表
End;

24位真彩色图形转化为ICO文件

  有了上面的基础知识,把24位真彩色图形转化为ICO文件就比较简单了,至于采用哪种编程语言,就看编程者的爱好了。下面笔者就采用Delphi实现本功能,进行详细介绍。

  上面讲的是把BMP格式的图像转换为ICO文件,因此,对于其他格式的图像我们要先把它转换为位图。在Delphi中我们可以采用如下方法:

procedure TFormMain.Pic2BMP(Picture:TPicture);
var Bmp:TBitmap;
begin
 if not(Picture.Graphic is TBitmap)then//判断是否是BMP图像
 begin
  Bmp:=TBitmap.Create;//不是BMP图形,就生成一个
  try
   Bmp.Width:=Picture.Width;
   bmp.Height:=Picture.Height;
   bmp.Canvas.Draw(0,0,Picture.Graphic);//把其他格式的图像复制到BMP
   Picture.Graphic:=Bmp;//原始非BMP图像转换为BMP图像
  Finally
   Bmp.Free;
  end;
 end;
end;

  有了BMP图像了,我们还要改变图像的长和宽,使它们符合要求的图表尺寸,注意不超出255。我们用如下方法实现尺寸的改变:

procedure TFormMain.PicToMiniature(SourceBMP, DescBMP:TBitmap; picH,picW :Integer);
var
 bmp: TBitmap;
begin
try
 bmp := TBitmap.Create;//生成位图
 bmp.Assign(SourceBMP);//位图图像为SourceBMP,
 if picW>255 then PicW:=255;//长宽不可超出255
  if picH>255 then picH:=255;
   bmp.Width := PicW;
   bmp.Height :=PicH;
   bmp.PixelFormat := pf24bit;//24位位图
   bmp.Canvas.StretchDraw(Rect(0,0,picW,picH), SourceBMP);//使位图尺寸符合要求
   DescBMP.Assign(bmp);
  finally
   bmp.Free;
  end;
end;

  生成位图的原料已经准备好了,就可以按ICO的文件头,关于程序的说明请看注释。

function TFormMain.MakICOHead(const Mem:TStream): Boolean;
var//采用流来生成
 BMPHead1:tagBITMAPFILEHEADER;
 BMPHead2: TBitmapInfoHeader;
 BitsTotal:DWord;
begin
 Result:=False;
 Mem.Position:=0;
 Mem.Read(BMPHead1,SizeOf(tagBITMAPFILEHEADER));//读取BMP文件由文件头
 Mem.Read(BMPHead2,SizeOf(TBitmapInfoHeader));// 读取BMP位图信息头
 if BMPHead2.biCompression=0 then //位图没有压缩
 begin
  if (BMPHead2.biWidth<=255) and (BMPHead2.biHeight <=255)then
  begin
   //caption:=IconFileName;
   IconHand.idEntries.bWidth:= Byte(BMPHead2.biWidth) ;//IOC宽
   IconHand.idEntries.bHeight:=Byte(BMPHead2.biHeight); //IOC高
   BitsTotal:=(Mem.Size-54)*2+40;
   //(BMP文件的大小- 文件头、位图信息头)*2+位图信息头=ICO数据量
   //乘以二的原因是:加icXOR的信息
   IconHand.idEntries.dwBytesInRes:= BitsTotal;
   IconHand.idEntries.dwImageOffset:=000016;
   Result:=True;
  end;
  Mem.Position:=0;
 end;
end;

  有了文件头,最终可以生成ICO了,同样详细内容请看程序的注释。

function TFormMain.MakICOData( Mem:TStream): Boolean;
var
 Mem1,Mem2:TMemoryStream;
 Size:Longint;
 BmtMapHandle2:TBitmapInfoHeader;
begin
 Mem1:=TMemoryStream.Create;
 Mem2:=TMemoryStream.Create;
 Size:=Mem.Size-14;//跳过14字节的BMP文件由文件头
 Mem.Position:=14;
 try
  Mem1.SetSize(Size);
  Mem.Read(Mem1.Memory^,Size);//BMP到Mem1
  Mem1.Seek(0,soFromBeginning);
  Mem1.Read(BmtMapHandle2,sizeof(TBitmapInfoHeader));//BMP文件的信息头
  Mem2.SetSize(Size-40);//跳过40字节的BMP文件信息头
  FillChar(Mem2.Memory^,Size-40,);//Mem2填充0 ,使掩码效果为白色
  Mem2.Position:=0;
  BmtMapHandle2.biHeight:=IconHand.idEntries.bHeight *2;//有两幅图
  BmtMapHandle2.biSizeImage:=Mem2.Size*2;
  Mem1.Seek(0,soFromBeginning);
  Mem1.Write(BmtMapHandle2,sizeof(TBitmapInfoHeader));
  Mem1.Position:=0;
  Mem.Size:=0;
  //MS.SetSize(0);
  Mem.Write(IconHand,sizeof(tagIconDir));//写ICO文件头
  Mem.Write(Mem1.Memory^,Mem1.Size);//写BMP片
  Mem.Write(Mem2.Memory^,Mem2.Size);//写掩码
  Result:=True;
  finally
  FreeAndNil(Mem1);
  FreeAndNil(Mem2);
 end;
end;

  结束语

  目前Delphi支持的图片格式比较多,例如我们可以给程序加上uses jpeg 语句就可以支持Jpeg格式的图像,当然如你给Delphi安装了支持其他图像格式的控件,使用本程序照样可以转换,得到的ICO文件可以供VB、Delphi等调用。程序的调试环境为Delphi7+WinxpSp2。
 
上面有部分代码没有声明和错误,请参照下面的代码
 

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, jpeg, ExtCtrls;

type
tagIconDirEntry = packed record
bWidth:BYTE;// 图标图片的显示宽度,以像素为单位,最大值为255 ($10=16D)
bHeight:BYTE;// 图标图片的显示高度,以像素为单位,最大值为255 ($10=16D)
bColorCount:BYTE;// 图标图片的颜色数($00)
bReserved:BYTE;// 保留域总是 0 ($00)
wPlanes:WORD;// 图标图片的位面数 ($00 00)
wBitCount:WORD;// 图标图片的颜色深度($00 00)
dwBytesInRes:DWORD;// 图标图片占用的数据量($00000568)
dwImageOffset:DWORD; // 图标图片的开始位置 ($00000046)
end;// 这个结构是16个字节
type
tagIconDir = packed record
idReserved:WORD;// 保留域,目前始终为 0(开始的数据$00 00)
idType:WORD; //定义为资源类型,图标值为 $0001、光标是$0002($0001)
idCount:WORD; //idCount 表示的是这个文件里包含了几个图标($0004)
idEntries:array[0..0] of tagIconDirEntry; //不包括本数组,以上一共6个字节
end;
type
tagIconImage = packed record
icHeader:TBitmapInfoHeader; //BMP文件的信息头
icColors:array[0..0]of TRGBQuad; 
icXOR:array[0..0]of BYTE; 
icAND:array[0..0]of BYTE; 
end;
type
tagBITMAPFILEHEADER= packed record
bfType:WORD; // 位图文件的类型,必须为BM
bfSize:DWORD; // 位图文件的大小,以字节为单位
bfReserved1:WORD; // 位图文件保留字,必须为0
bfReserved2:WORD; // 位图文件保留字,必须为0
bfOffBits:DWORD; // 位图数据的起始位置,以相对于位图
// 文件头的偏移量表示,以字节为单位
End;
type
tagRGBQUAD = packed record
rgbBlue:BYTE;// 蓝色的亮度(值范围为0-255)
rgbGreen:BYTE; // 绿色的亮度(值范围为0-255)
rgbRed:BYTE; // 红色的亮度(值范围为0-255)
rgbReserved:BYTE;// 保留,必须为0
end;
 type
tagBITMAPINFOHEADER= packed record
biSize:DWORD; // 本结构所占用字节数
biWidth:LONGINT; // 位图的宽度,以像素为单位
biHeight:LONGINT; // 位图的高度,以像素为单位
biPlanes:WORD; // 目标设备的级别,必须为1
biBitCount:WORD; // 每个像素所需的位数,必须是1(双色),
// 4(16色),8(256色)或24(真彩色)之一
biCompression :DWORD; // 位图压缩类型,必须是 0(不压缩),
// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
biSizeImage :DWORD; // 位图的大小,以字节为单位
biXPelsPerMeter:LONGINT; // 位图水平分辨率,每米像素数
biYPelsPerMeter:LONGINT; // 位图垂直分辨率,每米像素数
biClrUsed:DWORD;// 位图实际使用的颜色表中的颜色数
biClrImportant:DWORD;// 位图显示过程中重要的颜色数
End;
type
tagBITMAPINFO = packed record
bmiHeader:BITMAPINFOHEADER; // 位图信息头
bmiColors :array[0..0]of RGBQUAD; // 颜色表
End;
type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    BitBtn1: TBitBtn;

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
Iconhand:tagIconDir;

implementation
procedure Pic2BMP(Picture:TPicture);
var Bmp:TBitmap;
begin
if not(Picture.Graphic is TBitmap)then//判断是否是BMP图像
begin
Bmp:=TBitmap.Create;//不是BMP图形,就生成一个
try
Bmp.Width:=Picture.Width;
bmp.Height:=Picture.Height;
bmp.Canvas.Draw(0,0,Picture.Graphic);//把其他格式的图像复制到BMP
Picture.Graphic:=Bmp;//原始非BMP图像转换为BMP图像

Finally
Bmp.Free;
end;
end;
end;

procedure PicToMiniature(SourceBMP, DescBMP:TBitmap; picH,picW :Integer);
var
bmp: TBitmap;
begin
try
bmp := TBitmap.Create;//生成位图
bmp.Assign(SourceBMP);//位图图像为SourceBMP,
if picW>255 then PicW:=255;//长宽不可超出255
if picH>255 then picH:=255;
bmp.Width := PicW;
bmp.Height :=PicH;
bmp.PixelFormat := pf24bit;//24位位图
bmp.Canvas.StretchDraw(Rect(0,0,picW,picH), SourceBMP);//使位图尺寸符合要求
DescBMP.Assign(bmp);
finally
bmp.Free;
end;
end;

function MakICOHead(const Mem:TStream): Boolean;
var//采用流来生成
BMPHead1:tagBITMAPFILEHEADER;
BMPHead2: TBitmapInfoHeader;
BitsTotal:DWord;
begin
Result:=False;
Mem.Position:=0;
Mem.Read(BMPHead1,SizeOf(tagBITMAPFILEHEADER));//读取BMP文件由文件头
Mem.Read(BMPHead2,SizeOf(TBitmapInfoHeader));// 读取BMP位图信息头
if BMPHead2.biCompression=0 then //位图没有压缩
begin
if (BMPHead2.biWidth<=255) and (BMPHead2.biHeight <=255)then
begin
//caption:=IconFileName;
IconHand.idEntries[0].bWidth:= Byte(BMPHead2.biWidth);//IOC宽
IconHand.idEntries[0].bHeight:=Byte(BMPHead2.biHeight); //IOC高
BitsTotal:=(Mem.Size-54)*2+40;
//(BMP文件的大小- 文件头、位图信息头)*2+位图信息头=ICO数据量
//乘以二的原因是:加icXOR的信息
IconHand.idEntries[0].dwBytesInRes:= BitsTotal;
IconHand.idEntries[0].dwImageOffset:=$00000016;
Result:=True;
end;
Mem.Position:=0;
end;
end;

function MakICOData( Mem:TStream): Boolean;
var
Mem1,Mem2:TMemoryStream;
Size:Longint;
BmtMapHandle2:TBitmapInfoHeader;
begin
Mem1:=TMemoryStream.Create;
Mem2:=TMemoryStream.Create;
Size:=Mem.Size-14;//跳过14字节的BMP文件由文件头
Mem.Position:=14;
try
Mem1.SetSize(Size);
Mem.Read(Mem1.Memory^,Size);//BMP到Mem1
Mem1.Seek(0,soFromBeginning);
Mem1.Read(BmtMapHandle2,sizeof(TBitmapInfoHeader));//BMP文件的信息头
Mem2.SetSize(Size-40);//跳过40字节的BMP文件信息头
FillChar(Mem2.Memory^,Size-40,$0);//Mem2填充0 ,使掩码效果为白色
Mem2.Position:=0;
BmtMapHandle2.biHeight:=IconHand.idEntries[0].bHeight *2;//有两幅图
BmtMapHandle2.biSizeImage:=Mem2.Size*2;
Mem1.Seek(0,soFromBeginning);
Mem1.Write(BmtMapHandle2,sizeof(TBitmapInfoHeader));
Mem1.Position:=0;
Mem.Size:=0;
//MS.SetSize(0);
Mem.Write(IconHand,sizeof(tagIconDir){22});//写ICO文件头
Mem.Write(Mem1.Memory^,Mem1.Size);//写BMP片
Mem.Write(Mem2.Memory^,Mem2.Size);//写掩码
Result:=True;
finally
FreeAndNil(Mem1);
FreeAndNil(Mem2);
end;
end;
{$R *.dfm}

 
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多