icon是一种图标格式,用于系统图标、软件图标等,这种图标扩展名为*.icon、*.ico。常见的软件或windows桌面上的那些图标一般都是ICON格式的。
ICON文件格式比较简单,包含文件头段、图像数据头段、图像数据段。
文件头:
文件头为6个字节,定义如下:
1
2
3
4
5
6
|
type ICONDIR = packed record
idReserved: SmallInt ; // Reserved
idType: SmallInt ; // Resource type
idCount: SmallInt ; // Image Count
end ; // 6 bytes
|
idCount标记了文件中包含的图像数量。
图像数据头段:
紧接着文件头的就是图像数据头段了,它存放着文件中每个图像宽度、高度、颜色数量、数据段的偏移等信息,大小为16 * idCount。它是一个数组,每项数据16字节,定义如下:
type
ICONDIRENTRY = packed record
bWidth: Byte; // Width of the image
bHeight: Byte; // Height of the image (2 * Height)
bColorCount: Byte; // Number of colors in image (0 when >= 8 bpp)
bReserved: Byte; // Reserved
wPlanes: SmallInt; // Color Planes (-> xHotspot [Cursor])
wBitCount: SmallInt; // Bits per pixel (-> yHotspot [Cursor])
dwBytesInRes: Integer; // How many bytes in this resource?
dwImageOffset: Integer; // Where in the file is this image?
end; // 16 bytes
读取完图像数据头段后,就可以知道文件中每个图标的大小,颜色位数了,同时直接根据数据段的偏移,读取图像数据段。图像数据的偏移是从文件最开始算起的。
图像数据段:
图像数据为多个图像的DIB数据,根据数据头段的偏移来定位。定位后,读取BIH信息。从BIH信息中,判断颜色位数大于8位的,记取XOR调色盘数据(小于8位的不存在XOR调色盘,只包含有一个MASK调色盘)。读取完XOR调色盘后,初始化图像DIB头,然后在文件中读取DIB数据。
下面来看看实现的加载代码:(注意:本示例代码,只加载ICO文件中颜色位数最高,最大的一个)
相关定义:
type
dibBPPCts = (bpp_01 = 1, bpp_04 = 4, bpp_08 = 8, bpp_16 = 16, bpp_24 = 24,
bpp_32 = 32); DIBItem = packed record
private
function GetBPP: dibBPPCts;
function GetBytesPerScanline: Integer;
function GetHeight: Integer;
function GetWidth: Integer;
function GetSize: Integer;
public
m_uBIH: BITMAPINFOHEADER;
m_hDC: Integer;
m_hDIB: Integer;
m_hOldDIB: Integer;
m_lpBits: Pointer;
m_lpBitsSize: Integer; function Create(NewWidth, NewHeight: Integer; NewBPP: dibBPPCts): Boolean;
procedure Free();
procedure Clone(var toDIB: DIBItem); procedure GetPalette(var Palette: TBytes);
procedure SetPalette(Palette: TBytes); function Stretch(dc: HDC; x, y, w, h, xSrc, ySrc, wSrc, hSrc: Integer;
rop: Cardinal): Integer;
function Stretch32(dc: HDC; BufferBits: Pointer; BytesPerRow, x, y, w, h, xSrc, ySrc, wSrc, hSrc: Integer): Integer; property Width: Integer read GetWidth;
property Height: Integer read GetHeight;
property BPP: dibBPPCts read GetBPP;
property BytesPerScanline: Integer read GetBytesPerScanline;
property Size: Integer read GetSize;
end; DIBDATA = packed record
XORDIB: DIBItem; // XOR DIB section
ANDDIB: DIBItem; // AND DIB section
end; type
BITMAPINFO_001 = packed record
bmiHeader: BITMAPINFOHEADER;
bmiColors: array [0 .. 7] of Byte;
end; BITMAPINFO_004 = packed record
bmiHeader: BITMAPINFOHEADER;
bmiColors: array [0 .. 63] of Byte;
end; BITMAPINFO_008 = packed record
bmiHeader: BITMAPINFOHEADER;
bmiColors: array [0 .. 1023] of Byte;
end; BITMAPINFO_RGB = packed record
bmiHeader: BITMAPINFOHEADER;
end; type
icoBPPCts = (Colors_002 = 1, Colors_016 = 4, Colors_256 = 8, Color_True = 24,
Color_ARGB = 32);
加载代码:
procedure TYxdIcon.LoadFromStream(Stream: TStream);
var
nImg: Integer;
begin
// Get icon header
Fillchar(m_uDir, sizeof(m_uDir), 0);
Stream.Read(m_uDir, sizeof(m_uDir)); // Get icon entries
SetLength(m_uDirEntry, m_uDir.idCount);
Stream.Read(m_uDirEntry[0], sizeof(ICONDIRENTRY) * m_uDir.idCount); // Initialize arrays and monochrome palette
//SetLength(m_OrderKey, m_uDir.idCount);
//SetLength(m_uDIBData, m_uDir.idCount);
//SetLength(m_DIBData, m_uDir.idCount);
SetLength(aPalAND, 8);
FillChar(aPalAND[0], SizeOf(aPalAND), 0);
aPalAND[4] := $FF;
aPalAND[5] := $FF;
aPalAND[6] := $FF; // Get images
nMaxIndex := -1;
nMaxW := 0;
for nImg := 0 to m_uDir.idCount - 1 do begin
if (m_uDirEntry[nImg].bWidth > nMaxW) or
((m_uDirEntry[nImg].bWidth = nMaxW) and (m_uDirEntry[nImg].bColorCount = 0)) then begin
nMaxW := m_uDirEntry[nImg].bWidth;
nMaxIndex := nImg;
end;
end; if nMaxIndex > -1 then begin
// Move to begin of image data
Stream.Position := m_uDirEntry[nMaxIndex].dwImageOffset;
// Load BITMAPINFOHEADER
Stream.Read(uBIH, SizeOf(uBIH));
// Load XOR palette [?] (<= 8 bpp)
if uBIH.biBitCount <= 8 then begin
SetLength(aPalXOR, 4 * Trunc(Power(2, uBIH.biBitCount)));
Stream.Read(aPalXOR[0], Length(aPalXOR));
end; // Inititalize XOR DIB
FillChar(m_uDIBData, SizeOf(m_uDIBData), 0);
m_uDIBData.XORDIB.Create(uBIH.biWidth, uBIH.biHeight div 2, dibBPPCts(ubih.biBitCount));
if uBIH.biBitCount <= 8 then
m_uDIBData.XORDIB.SetPalette(aPalXOR); // Inititalize AND DIB
m_uDIBData.ANDDIB.Create(uBIH.biWidth, uBIH.biHeight div 2, bpp_01);
m_uDIBData.ANDDIB.SetPalette(aPalAND); // Read DIB bits
m_uDIBData.XORDIB.m_lpBits := GetMemory(m_uDIBData.XORDIB.Size);
m_uDIBData.ANDDIB.m_lpBits := GetMemory(m_uDIBData.ANDDIB.Size);
Stream.Read(m_uDIBData.XORDIB.m_lpBits^, m_uDIBData.XORDIB.Size);
Stream.Read(m_uDIBData.ANDDIB.m_lpBits^, m_uDIBData.ANDDIB.Size); m_OrderKey := IntToHex(uBIH.biWidth, 3) + IntToHex(uBIH.biHeight div 2, 3) +
IntToHex(uBIH.biBitCount, 2);
end;
Changed(Self);
end;
DIBItem 就是ICON文件中一个图标的图像数据,代码如下:
{ DIBItem } procedure DIBItem.Clone(var toDIB: DIBItem);
var
aPal: TBytes;
begin
if m_hDIB <> 0 then begin
toDIB.Create(m_uBIH.biWidth, m_uBIH.biHeight, dibBPPCts(m_uBIH.biBitCount));
if m_lpBits <> nil then begin
toDIB.m_lpBits := GetMemory(Size);
CopyMemory(toDIB.m_lpBits, m_lpBits, Size);
end else
toDIB.m_lpBits := nil;
if (m_uBIH.biBitCount <= 8) then begin
SetLength(aPal, 4 * Trunc(Power(2, m_uBIH.biBitCount)) - 1);
GetPalette(aPal);
toDIB.SetPalette(aPal);
end;
end;
end; function DIBItem.Create(NewWidth, NewHeight: Integer;
NewBPP: dibBPPCts): Boolean;
var
BI_001: BITMAPINFO_001;
BI_004: BITMAPINFO_004;
BI_008: BITMAPINFO_008;
BI_RGB: BITMAPINFO_RGB;
begin
Free(); // Define DIB header
m_uBIH.biSize := SizeOf(m_uBIH);
m_uBIH.biPlanes := 1;
m_uBIH.biBitCount := Integer(NewBPP);
m_uBIH.biWidth := NewWidth;
m_uBIH.biHeight := NewHeight;
m_uBIH.biSizeImage := 4 * ((m_uBIH.biWidth * m_uBIH.biBitCount + 31) div 32) * m_uBIH.biHeight;
case NewBPP of
bpp_01: BI_001.bmiHeader := m_uBIH;
bpp_04: BI_004.bmiHeader := m_uBIH;
bpp_08: BI_008.bmiHeader := m_uBIH;
else
BI_RGB.bmiHeader := m_uBIH
end; // Create DIB and select into a DC
m_hDC := CreateCompatibleDC(0);
if m_hDC <> 0 then begin
case NewBPP of
bpp_01: m_hDIB := CreateDIBSection(m_hDC, pBitmapInfo(@BI_001)^, DIB_RGB_COLORS, m_lpBits, 0, 0);
bpp_04: m_hDIB := CreateDIBSection(m_hDC, pBitmapInfo(@BI_004)^, DIB_RGB_COLORS, m_lpBits, 0, 0);
bpp_08: m_hDIB := CreateDIBSection(m_hDC, pBitmapInfo(@BI_008)^, DIB_RGB_COLORS, m_lpBits, 0, 0);
else
m_hDIB := CreateDIBSection(m_hDC, pBitmapInfo(@BI_RGB)^, DIB_RGB_COLORS, m_lpBits, 0, 0);
end;
if m_hDIB <> 0 then
m_hOldDIB := SelectObject(m_hDC, m_hDIB)
else
Free;
end;
Result := m_hDIB <> 0;
end; procedure DIBItem.Free;
begin
if m_hDC <> 0 then begin
if m_hDIB <> 0 then begin
SelectObject(m_hDC, m_hOldDIB);
DeleteObject(m_hDIB);
end;
DeleteDC(m_hDC);
end;
if m_lpBits <> nil then begin
FreeMemory(m_lpBits);
m_lpBits := nil;
end;
FillChar(m_uBIH, SizeOf(m_uBIH), 0);
m_hDC := 0;
m_hDIB := 0;
m_hOldDIB := 0;
end; function DIBItem.GetBPP: dibBPPCts;
begin
Result := dibBPPCts(m_uBIH.biBitCount);
end; function DIBItem.GetBytesPerScanline: Integer;
begin
Result := ((m_uBIH.biWidth * m_uBIH.biBitCount + 31) div 32) * 4;
end; function DIBItem.GetHeight: Integer;
begin
Result := m_uBIH.biHeight;
end; procedure DIBItem.GetPalette(var Palette: TBytes);
begin
if m_hDIB <> 0 then
GetDIBColorTable(m_hDC, 0, Trunc(Power(2, m_uBIH.biBitCount)), Palette[Low(Palette)]);
end; function DIBItem.GetSize: Integer;
begin
Result := m_uBIH.biSizeImage;
end; function DIBItem.GetWidth: Integer;
begin
Result := m_uBIH.biWidth;
end; procedure DIBItem.SetPalette(Palette: TBytes);
begin
SetDIBColorTable(m_hDC, 0, (High(Palette) - Low(Palette) + 1) div 4, Palette[Low(Palette)])
end; function DIBItem.Stretch(dc: HDC; x, y, w, h, xSrc, ySrc, wSrc, hSrc: Integer;
rop: Cardinal): Integer;
var
b001: BITMAPINFO_001;
b004: BITMAPINFO_004;
b008: BITMAPINFO_008;
brgb: BITMAPINFO_RGB;
iLen: Integer;
lOldMode: Integer;
begin
Result := 0;
if m_hDIB = 0 then Exit;
lOldMode := SetStretchBltMode(dc, COLORONCOLOR);
iLen := Trunc(Power(2, m_uBIH.biBitCount));
case dibBPPCts(m_uBIH.biBitCount) of
bpp_01:
begin
b001.bmiHeader := m_uBIH;
GetDIBColorTable(m_hDC, 0, iLen, b001.bmiColors[0]);
StretchDIBits(dc, x, y, w, h, xSrc, ySrc, wsrc, hsrc, m_lpBits,
pBitmapinfo(@b001)^, DIB_RGB_COLORS, rop);
end;
bpp_04:
begin
b004.bmiHeader := m_uBIH;
GetDIBColorTable(m_hDC, 0, iLen, b004.bmiColors[0]);
StretchDIBits(dc, x, y, w, h, xSrc, ySrc, wsrc, hsrc, m_lpBits,
pBitmapinfo(@b004)^, DIB_RGB_COLORS, rop);
end;
bpp_08:
begin
b008.bmiHeader := m_uBIH;
GetDIBColorTable(m_hDC, 0, iLen, b008.bmiColors[0]);
StretchDIBits(dc, x, y, w, h, xSrc, ySrc, wsrc, hsrc, m_lpBits,
pBitmapinfo(@b008)^, DIB_RGB_COLORS, rop);
end;
else begin
brgb.bmiHeader := m_uBIH;
StretchDIBits(dc, x, y, w, h, xSrc, ySrc, wsrc, hsrc, m_lpBits,
pBitmapinfo(@brgb)^, DIB_RGB_COLORS, rop);
end;
end;
SetStretchBltMode(dc, lOldMode);
Result := 1;
end; function DIBItem.Stretch32(dc: HDC; BufferBits: Pointer; BytesPerRow, x, y, w, h, xSrc, ySrc, wSrc, hSrc: Integer): Integer;
var
i, i2, j, j2: Integer;
Stretch: Boolean;
FactorX, FactorY: Double;
ABytesPerScanline: Integer;
AlphaSource, ImageData: pPixelLine;
begin
Result := 0;
if (m_hDIB = 0) or (m_lpBits = nil) then Exit; Stretch := (W <> wSrc) or (H <> hSrc);
if Stretch then FactorX := W / wSrc else FactorX := 1;
if Stretch then FactorY := H / hSrc else FactorY := 1; AlphaSource := m_lpBits;
ABytesPerScanline := BytesPerScanline;
PByte(ImageData) := PByte(Integer(BufferBits) + BytesPerRow * (H - 1));
BufferBits := ImageData; if m_uBIH.biBitCount = 32 then begin FOR j := 1 TO H DO begin
FOR i := 0 TO W - 1 DO begin
if Stretch then i2 := trunc(i / FactorX) else i2 := i;
if (AlphaSource[i2].rgbReserved <> 0) then begin
if (AlphaSource[i2].rgbReserved = 255) then begin
ImageData[i] := AlphaSource[i2];
end else
with ImageData[i] do begin
rgbRed := ($7F + AlphaSource[i2].rgbRed * AlphaSource[i2].rgbReserved + rgbRed *
(not AlphaSource[i2].rgbReserved)) div $FF;
rgbGreen := ($7F + AlphaSource[i2].rgbGreen * AlphaSource[i2].rgbReserved +
rgbGreen * (not AlphaSource[i2].rgbReserved)) div $FF;
rgbBlue := ($7F + AlphaSource[i2].rgbBlue * AlphaSource[i2].rgbReserved + rgbBlue *
(not AlphaSource[i2].rgbReserved)) div $FF;
rgbReserved := not (($7F + (not rgbReserved) * (not AlphaSource[i2].rgbReserved)) div $FF);
end;
end;
end; {Move pointers}
PByte(ImageData) := PByte(Integer(BufferBits) - BytesPerRow * j);
if Stretch then j2 := trunc(j / FactorY) else j2 := j;
PByte(AlphaSource) := PByte(m_lpBits) + ABytesPerScanline * j2;
end
end;
Result := 1;
end;
ICON的显示:
本示例中的TYxdIcon继承自TIcon,本身就是一个Graphic了,所以可以直接重载Draw实现显示。
type
TYxdIcon = class(TIcon)
private
m_uDir: ICONDIR; // Icon file header
m_uDirEntry: array of ICONDIRENTRY; // Icon image headers
m_OrderKey: string; // Image format key
aPalXOR: TBytes;
aPalAND: TBytes;
nMaxW: Byte;
nMaxIndex: Integer;
uBIH: BITMAPINFOHEADER; BufferDC: HDC;
OldBufferBitmap, BufferBitmap: HBitmap;
LastW, LastH: Integer;
FUseBuffer: Boolean;
protected
m_uDIBData: DIBDATA; // Icon data (DIBs)
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
function GetSupportsPartialTransparency: Boolean; override;
public
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
property UseBuffer: Boolean read FUseBuffer write FUseBuffer;
end;
procedure TYxdIcon.Draw(ACanvas: TCanvas; const Rect: TRect);
var
BitmapInfo: TBitmapInfo;
BufferBits, Buf: Pointer;
AlphaSource, ImageData: pPixelLine;
W, H: Integer;
BytesPerRow: Integer;
I, J: Integer;
begin
W := Rect.Right - Rect.Left;
H := Rect.Bottom - Rect.Top; BitmapInfo := GetBitmapInfoHeader(W, H);
BufferDC := CreateCompatibleDC(0);
if (BufferDC = 0) then Exit; BytesPerRow := (((BitmapInfo.bmiHeader.biBitCount * W) + 31) and
not 31) div 8; BufferBitmap := CreateDIBSection(BufferDC, pBitmapInfo(@BitmapInfo)^,
DIB_RGB_COLORS, BufferBits, 0, 0);
OldBufferBitmap := SelectObject(BufferDC, BufferBitmap);
BitBlt(BufferDC, 0, 0, W, H, ACanvas.Handle, Rect.Left, Rect.Top, SRCCOPY); if m_uDIBData.XORDIB.BPP = bpp_32 then begin
m_uDIBData.XORDIB.Stretch32(BufferDC, BufferBits, BytesPerRow, Rect.Left, Rect.Top,
W, H, 0, 0, m_uDIBData.XORDIB.Width, m_uDIBData.XORDIB.Height);
end else begin
// 画 Mask 层
m_uDIBData.ANDDIB.Stretch(BufferDC, Rect.Left, Rect.Top, W, H, 0, 0,
m_uDIBData.XORDIB.Width, m_uDIBData.XORDIB.Height, SRCCOPY); // 保存透明区域信息
Buf := GetMemory(BytesPerRow*H);
CopyMemory(Buf, BufferBits, BytesPerRow*H); // 画实际图像
m_uDIBData.XORDIB.Stretch(BufferDC, Rect.Left, Rect.Top, W, H, 0, 0,
m_uDIBData.XORDIB.Width, m_uDIBData.XORDIB.Height, SRCCOPY); // 应用 Mask
PByte(ImageData) := PByte(BufferBits);
AlphaSource := Buf;
FOR j := 1 TO H DO begin
FOR i := 0 TO W - 1 DO begin
if (AlphaSource[i].rgbBlue = 0) and (AlphaSource[i].rgbGreen = 0) and (AlphaSource[i].rgbRed = 0) then begin
ImageData[i].rgbReserved := $ff;
end else
ImageData[i].rgbReserved := 0;
end;
{Move pointers}
inc(PByte(AlphaSource), BytesPerRow);
inc(PByte(ImageData), BytesPerRow);
end;
FreeMemory(Buf);
end; TYxdCanvas(ACanvas).RequiredState([csHandleValid]);
BitBlt(ACanvas.Handle, Rect.Left, Rect.Top, W, H, BufferDC, 0, 0, SRCCOPY); SelectObject(BufferDC, OldBufferBitmap);
DeleteObject(BufferBitmap);
DeleteDC(BufferDC);
BufferBitmap := 0;
end;
显示中主要特别处理的地方就是区分是否为32位的带透明通道图标。不带透明通道的,使用MASK层进行透明处理。
http://www.cnblogs.com/yangyxd/articles/3984901.html