Delphi 內建的 Image Editor 直到 Delphi 7 仍不支援真彩色圖標編輯,這是一大遺憾。本文深入分析 ICO 檔案格式與 BMP 位圖結構,實現從 JPEG、BMP 等 24 位真彩色圖片產生圖標檔的完整方案。
一個 ICO 檔可包含多個不同尺寸/色深的圖標影像。檔案結構包含:
tagIconDir):6 位元組,含保留欄位、資源類型(1=圖標)、圖標數量tagIconDirEntry):每個圖標 16 位元組,記錄寬、高、色數、色深、資料大小、資料偏移位置tagIconImage):實際位圖資料,含 TBitmapInfoHeader、調色盤、XOR 遮罩、AND 遮罩關鍵點:圖標顯示時使用雙重遮罩機制(XOR 切出區域、AND 繪製圖形),因此 biHeight 等於兩張位圖高度之和。
標準 BMP 包含四段:
tagBITMAPFILEHEADER):14 位元組,標識 "BM"、檔案大小、資料偏移tagBITMAPINFOHEADER):40 位元組,寬高、色深、壓縮類型、解析度等RGBQUAD 陣列):1/4/8 位色深時分別有 2/16/256 色;24 位真彩色無調色盤// 1. 非 BMP 格式先轉為 BMP
procedure Pic2BMP(Picture: TPicture);
var Bmp: TBitmap;
begin
if not (Picture.Graphic is TBitmap) then begin
Bmp := TBitmap.Create;
try
Bmp.Width := Picture.Width;
Bmp.Height := Picture.Height;
Bmp.Canvas.Draw(0, 0, Picture.Graphic);
Picture.Graphic := Bmp;
finally Bmp.Free; end;
end;
end;
// 2. 調整尺寸至規格 (不超過 255x255)
procedure PicToMiniature(SourceBMP, DestBMP: TBitmap; H, W: Integer);
var Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.Assign(SourceBMP);
if W > 255 then W := 255;
if H > 255 then H := 255;
Bmp.Width := W; Bmp.Height := H;
Bmp.PixelFormat := pf24bit;
Bmp.Canvas.StretchDraw(Rect(0,0,W,H), SourceBMP);
DestBMP.Assign(Bmp);
finally Bmp.Free; end;
end;
// 3. 建立 ICO 檔案標頭
function MakICOHead(const Mem: TStream): Boolean;
var BMPHead1: tagBITMAPFILEHEADER;
BMPHead2: TBitmapInfoHeader;
BitsTotal: DWORD;
begin
Result := False;
Mem.Position := 0;
Mem.Read(BMPHead1, SizeOf(tagBITMAPFILEHEADER));
Mem.Read(BMPHead2, SizeOf(TBitmapInfoHeader));
if (BMPHead2.biCompression = 0) and (BMPHead2.biWidth <= 255) and (BMPHead2.biHeight <= 255) then begin
IconHead.idEntries.bWidth := Byte(BMPHead2.biWidth);
IconHead.idEntries.bHeight := Byte(BMPHead2.biHeight);
BitsTotal := (Mem.Size - 54) * 2 + 40; // *2 含 XOR 遮罩
IconHead.idEntries.dwBytesInRes := BitsTotal;
IconHead.idEntries.dwImageOffset := $16;
Result := True;
end;
Mem.Position := 0;
end;
// 4. 產生 ICO 完整資料 (含遮罩)
function MakICOData(Mem: TStream): Boolean;
var Mem1, Mem2: TMemoryStream;
Size: Longint;
BMIH2: TBitmapInfoHeader;
begin
Mem1 := TMemoryStream.Create;
Mem2 := TMemoryStream.Create;
try
Size := Mem.Size - 14; Mem.Position := 14;
Mem1.SetSize(Size);
Mem.Read(Mem1.Memory^, Size); // 複製 BMP 資料 (含資訊頭)
Mem1.Read(BMIH2, SizeOf(TBitmapInfoHeader));
Mem2.SetSize(Size - 40); // 跳過資訊頭
FillChar(Mem2.Memory^, Size - 40, 0); // 遮罩填 0 = 白色背景
BMIH2.biHeight := IconHead.idEntries.bHeight * 2; // 雙倍高度
BMIH2.biSizeImage := Mem2.Size * 2;
Mem1.Seek(0, soFromBeginning);
Mem1.Write(BMIH2, SizeOf(TBitmapInfoHeader)); // 更新資訊頭
Mem.Size := 0;
Mem.Write(IconHead, SizeOf(tagIconDir)); // 寫 ICO 標頭
Mem.Write(Mem1.Memory^, Mem1.Size); // 寫 XOR 影像
Mem.Write(Mem2.Memory^, Mem2.Size); // 寫 AND 遮罩
Result := True;
finally
Mem1.Free; Mem2.Free;
end;
end;
Delphi 支援豐富圖片格式(加入 uses jpeg 即支援 JPEG),若安裝第三方圖形元件亦可擴展。產出的 ICO 可供 VB、Delphi 等直接使用。測試環境:Delphi 7 + Windows XP SP2。