程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi實現24位真彩色圖標

Delphi實現24位真彩色圖標

編輯:Delphi

引言

Delphi是目前廣泛使用的可視化開發工具,它自身帶有一個圖片、圖標的編輯器——Image Editor,但是到Delphi7為止,都不能進行真彩圖標的編輯,可以說是一個遺憾。筆者通過對圖標文件的研究,實現了產生24位真彩色圖標。

圖標文件的格式

首先,分析一個具體的圖標 。在CS1.6中有一個圖標game.ico( ),如果用WinHex等可以進行16進制編輯的軟件打開這個圖標文件,我們可以看到如下數據:

00 00 01 00 04 00 10 10 00 00 00 00 00 00 68 05

00 00 46 00 00 00 10 10 00 00 00 00 00 00 68 03

00 00 AE 05 00 00 20 20 00 00 00 00 00 00 A8 08

00 00 16 09 00 00 20 20 00 00 00 00 00 00 A8 0C

00 00 BE 11 00 00 28 00 00 00 10 00 00 00 20 00

00 00 01 00 08 00 00 00 00 00 40 01 00 00 47 46

6C 65 6D 69 6E 67 00 01 00 00 00 00 00 00 00 00

下面我們就說一說,這些數據的具體含義。一個圖標文件(*.ICO),實際上可以含有多個圖標.通常,每個圖標都會被轉換為針對特定顯示設備的圖標圖像。圖標文件由文件頭和數據組成, ICO文件一開始,是一個叫做tagIconDir的記錄型的結構,在Delphi中這樣來描述(括號內的數值,是針對CS圖標的具體數據):

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;

這個記錄中的idEntries 是個數組結構,這個結構的大小不是始終為 1 的一個數組,它需要根據圖標數目 ( idCount ) 來確定真實的數組大小。它的類型為tagIconDirEntry記錄,定義如下:

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個字節

上面說的idCount 表示圖標文件裡包含的圖標個數,每個圖標都要有一個tagIconDirEntry結構來表示圖標的具體信息。根據本結構的dwBytesInRes和dwImageOffset我們就可以確定圖片(圖標)的位置了。在該位置的數據是一個稱為agIconImage的記錄,它是這樣定義的:

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;

從這個定義中我們可以看出,這個內容就是一個標准的位圖格式,只不過多了兩項,icXOR和icAND,普通的位圖信息裡是沒有這2 個成員的。大家知道,圖標在被顯示時,是利用遮罩方法將 2 副位圖在同一個位置顯示才產生任意輪廓的,先使用 XOR 位 圖摳出需要顯示的區域,然後再在摳出的區域中顯示出需要顯示的圖形。由於這個緣故,圖標的位圖格式中的位圖信息頭 ( TBitmapInfoHeader ) 是 2 個位圖共用 的。它與普通位圖頭信息最大的不同是 TBitmapInfoHeader.biHeight 成員,顯然它是 2 副位圖高度的總和。講到這裡,我們需要對位圖(BMP)文件的格式有些了解了。

位圖文件的格式

BMP文件由文件頭、位圖信息頭、顏色信息和圖形數據四部分組成。按照微軟的定義,在開始的文件頭由14個字節組成:

tagBITMAPFILEHEADER= packed record
bfType:WORD; // 位圖文件的類型,必須為BM
bfSize:DWORD; // 位圖文件的大小,以字節為單位
bfReserved1:WORD; // 位圖文件保留字,必須為0
bfReserved2:WORD; // 位圖文件保留字,必須為0
bfOffB its:DWORD; // 位圖數據的起始位置,以相對於位圖
// 文件頭的偏移量表示,以字節為單位
End;

緊接著上一記錄的是位圖信息頭tagBITMAPINFOHEADER,BMP位圖信息頭數據用於說明位圖的尺寸等信息。這個信息頭就是上文說的TBitmapInfoHeader,它的長度固定為40字節。

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;

緊接著就是顏色表,用於說明位圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQUAD結構的定義如下:

tagRGBQUAD = packed record
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 = packed record
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:=$00000016;
    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,$0);//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){22});//寫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。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved