本文整理自网络,侵删。
昨天在使用FreeImage的时候发现FreeImage对ICON的处理有问题,32位的帧读出来的背景居然是黑的。我猜应该是32位没有And的Mask数据。而它按照24位以下的处理方式把Alpha值填成255了。
只能自己动手写代码,也不复杂,算上把Windows单元抽出来声明的结构体,也就300行。经测试可以在Win32,Win64,Android,FMX for Linux上可以正常使用。
如此的简单,就不上传工程了,直接贴代码就是了。
unit FMX.Images.icon; { wr960204武稀松 2017.4.18 FMX读取ICO,ICON文件各个帧到TBitmap的。 经测试支持Win32, Win64,Android,FMX for Linux}interface uses System.Types, // Winapi.Windows, System.Classes, System.sysutils, FMX.Graphics, System.Generics.Collections; const PNG_Signature = $0A1A0A0D474E5089; type // 从WinAPi.Windows单元拷贝过来的。为了不引用Windows单元,以便跨平台 tagBITMAPINFOHEADER = record biSize: DWORD; biWidth: DWORD; biHeight: DWORD; biPlanes: Word; biBitCount: Word; biCompression: DWORD; biSizeImage: DWORD; biXPelsPerMeter: DWORD; biYPelsPerMeter: DWORD; biClrUsed: DWORD; biClrImportant: DWORD; end; TBitmapInfoHeader = tagBITMAPINFOHEADER; BITMAPINFOHEADER = tagBITMAPINFOHEADER; PRGBTriple = ^TRGBTriple; tagRGBTRIPLE = packed record rgbtBlue: Byte; rgbtGreen: Byte; rgbtRed: Byte; end; TRGBTriple = tagRGBTRIPLE; RGBTRIPLE = tagRGBTRIPLE; PRGBQuad = ^TRGBQuad; tagRGBQUAD = packed record rgbBlue: Byte; rgbGreen: Byte; rgbRed: Byte; rgbReserved: Byte; end; TRGBQuad = tagRGBQUAD; RGBQUAD = tagRGBQUAD; TIconDirEntry = packed record bWidth: Byte; bHeight: Byte; bColorCount: Byte; bReserved: Byte; wPlanes: Word; wBitCount: Word; dwBytesInRes: DWORD; dwImageOffset: DWORD; end; // Icon的结构体 PIconDirEntry = ^TIconDirEntry; TIconDir = packed record idReserved: Word; idType: Word; idCount: Word; idEntries: array [0 .. $0] of TIconDirEntry; end; PIconDir = ^TIconDir; TICONIMAGE = packed record icHeader: BITMAPINFOHEADER; // ptr to header { icColors: array [0 .. ColorCount] of RGBQUAD; // icXOR: array [0 .. bWidth * bHeight] of Byte; // icAND: array [0 .. bWidth * bHeight / bColorCount] of Byte; } end; TICONIMAGEicColors = packed record icColors: array [0 .. $FFFF] of RGBQUAD; // end; TICONIMAGEicXOR = packed record icXOR: array [0 .. $FFFF] of Byte; // end; TICONIMAGEicAND = packed record icAND: array [0 .. $FFFF] of Byte; end; PICONIMAGE = ^TICONIMAGE; PTICONIMAGEicColors = ^TICONIMAGEicColors; PICONIMAGEicXOR = ^TICONIMAGEicXOR; PICONIMAGEicAND = ^TICONIMAGEicAND; TBmpList = TObjectList<Tbitmap>; function LoadIcon(AFileName: string; Abmps: TBmpList): Boolean;function LoadIconByStream(AStream: TStream; Abmps: TBmpList): Boolean;function LoadIconByBuf(ABuf: PBYTE; ACount: Integer; Abmps: TBmpList): Boolean; implementation function LoadIcon(AFileName: string; Abmps: TBmpList): Boolean;var fs: TFileStream;begin fs := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); Result := LoadIconByStream(fs, Abmps); fs.Free;end; function LoadIconByStream(AStream: TStream; Abmps: TBmpList): Boolean;var bs: TBytes; ICONDIR: PIconDir; begin Result := False; SetLength(bs, AStream.Size); AStream.Read(bs[0], Length(bs)); Result := LoadIconByBuf(@bs[0], Length(bs), Abmps); end; {$POINTERMATH ON} function LoadPngFrame(ABuf: PBYTE; ACount: Integer; Abmp: Tbitmap): Boolean;var ms: TMemoryStream;begin ms := TMemoryStream.Create; ms.Write(ABuf^, ACount); ms.Position := 0; Abmp.LoadFromStream(ms); ms.Free; Result := True;end; function LoadIconFrame(lpIconDir: PIconDir; lpIconDirEntry: PIconDirEntry; lpIconImage: PICONIMAGE; Abmp: Tbitmap): Boolean;var width, height, realBitsCount: Integer; x, y: Integer; hasAndMask: Boolean; lpColors: PRGBQuad; lpColorsTriple: PRGBTriple ABSOLUTE lpColors; lpColors256: PBYTE ABSOLUTE lpColors; lpXor: PBYTE; lpAnd: PBYTE; // colorMapSize, XorSize, AndSize: Integer; boundary, shift, shift2, index, bit, mask: Integer; // D: TBitmapData; image: PRGBQuad;begin Result := False; {不能用lpIconDirEntry中的高和宽,教训是有些ICO文件不规范,这里的高和宽是0, 所致最好用lpIconImage^.icHeader中的高和宽 } width := lpIconImage^.icHeader.biWidth; height := lpIconImage^.icHeader.biHeight div 2; //高度等于 xor + and mask realBitsCount := lpIconImage^.icHeader.biBitCount; hasAndMask := (realBitsCount < 32) and (height <> lpIconImage^.icHeader.biHeight); colorMapSize := SizeOf(RGBQUAD) * (1 shl realBitsCount); if lpIconImage^.icHeader.biSize = 0 then lpColors := PRGBQuad(UInt64(lpIconImage) + SizeOf(TICONIMAGE) { 40 } ) else lpColors := PRGBQuad(UInt64(lpIconImage) + lpIconImage^.icHeader.biSize { 40 } ); lpXor := PBYTE(UInt64(lpColors) + colorMapSize); Abmp.Resize(width, height); if Abmp.Map(TMapAccess.Write, D) then begin case realBitsCount of 32: // 32位色 begin for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; shift2 := (x + (height - y - 1) * width); { image[shift].rgbBlue := lpColors[shift2].rgbBlue; image[shift].rgbGreen := lpColors[shift2].rgbGreen;; image[shift].rgbRed := lpColors[shift2].rgbRed; image[shift].rgbReserved := 255;//估计是Free Image这里填写的255.应该用32位色自己的.所以FreeImage拿出来的32位Frame背景居然是黑的 } image[shift] := lpColors[shift2]; end; end; end; 24: // 24位色 begin for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; shift2 := (x + (height - y - 1) * width); image[shift].rgbBlue := lpColorsTriple[shift2].rgbtBlue; image[shift].rgbGreen := lpColorsTriple[shift2].rgbtGreen;; image[shift].rgbRed := lpColorsTriple[shift2].rgbtRed; image[shift].rgbReserved := 255; end; end; end; 8: // 256色 begin for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; shift2 := (x + (height - y - 1) * width); index := lpXor[shift2]; image[shift].rgbBlue := lpColors[index].rgbBlue; image[shift].rgbGreen := lpColors[index].rgbGreen; image[shift].rgbRed := lpColors[index].rgbRed; image[shift].rgbReserved := 255; end; end; end; 4: // 16色 begin for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; shift2 := (x + (height - y - 1) * width); Index := lpXor[shift2 div 2]; if (shift2 mod 2) = 0 then Index := (Index shr 4) and $F else Index := Index and $F; image[shift].rgbBlue := lpColors[Index].rgbBlue; image[shift].rgbGreen := lpColors[Index].rgbGreen; image[shift].rgbRed := lpColors[Index].rgbRed; image[shift].rgbReserved := 255; end; end; end; 1: // 两色 begin boundary := width; while (boundary mod 32) <> 0 do Inc(boundary); // 32bit对齐 // for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; shift2 := (x + (height - y - 1) * boundary); index := lpXor[shift2 div 8]; bit := 7 - (x mod 8); index := (index shr bit) and $01; image[shift].rgbBlue := lpColors[index].rgbBlue; image[shift].rgbGreen := lpColors[index].rgbGreen;; image[shift].rgbRed := lpColors[index].rgbRed; image[shift].rgbReserved := 255; end; end; end; end; // if False then if hasAndMask then begin // 定位AndMask的位置 boundary := width * realBitsCount; // 换算到位 while (boundary mod 32) <> 0 do Inc(boundary); lpAnd := lpXor + (boundary * height div 8); // div 8 换算到字节 // 计算对齐的宽度 boundary := width; while (boundary mod 32) <> 0 do Inc(boundary); // 32bit对齐 for y := 0 to height - 1 do begin image := D.GetScanline(y); for x := 0 to width - 1 do begin shift := x; bit := 7 - (x mod 8); shift2 := (x + (height - y - 1) * boundary) div 8; mask := ($01 and (lpAnd[shift2] shr bit)); image[shift].rgbReserved := image[shift].rgbReserved * (1 - mask); end; end; end; Abmp.Unmap(D); end; end; function LoadIconByBuf(ABuf: PBYTE; ACount: Integer; Abmps: TBmpList): Boolean;var ICONDIR: PIconDir ABSOLUTE ABuf; iconDirEntry: PIconDirEntry; IconImage: PICONIMAGE; i: Integer; isPngFormat: Boolean; lBmp: Tbitmap;begin Result := False; if (ICONDIR^.idReserved <> 0) or (ICONDIR^.idType <> 1) or (ICONDIR^.idCount <= 0) then Exit; for i := 0 to ICONDIR^.idCount - 1 do begin iconDirEntry := @ICONDIR^.idEntries[i]; IconImage := PICONIMAGE(@ABuf[iconDirEntry.dwImageOffset]); // 新格式的ICON里面可以包含PNG图片 isPngFormat := PNG_Signature = PUint64(IconImage)^; lBmp := Tbitmap.Create; if isPngFormat then begin LoadPngFrame(PBYTE(IconImage), iconDirEntry^.dwBytesInRes, lBmp); end else begin LoadIconFrame(ICONDIR, iconDirEntry, IconImage, lBmp); end; Abmps.Add(lBmp); Result := True; end;end;{$POINTERMATH OFF} end.
来源:http://www.raysoftware.cn/?p=589
相关阅读 >>
Delphi deletedirectory 删除目录下所有文件包括子文件夹下所有文件
Delphi实现获取文件及文件夹大小(支持超过2g的大文件)
Delphi调用winapi: getsystemmetrics - 获取系统度量等数值信息
更多相关阅读请进入《Delphi》频道 >>