跨平台的,在FMX中读取ICON文件的每一帧到Bitmap

昨天在使用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.

此条目发表在Delphi, 未分类分类目录。将固定链接加入收藏夹。

跨平台的,在FMX中读取ICON文件的每一帧到Bitmap》有197条回应

  1. lutiexin说:

    你好,有个问题想请教下,冒昧写在这里了。
    我的程序是EXE+DLL结构,在DLL里面我写了一些基本的异常处理代码。
    其他的异常我想在ApplicationEvents.OnException事件里面处理,
    但是我在这个事件里面,怎么才能知道,是哪个DLL文件引发的异常呢?
    我查了一下,System.ExceptAddr这个变量是引发异常的地址,根据这个我是否可以知道是哪个DLL文件,甚至是哪个函数吗?
    如蒙回复邮件,不胜感激!!谢谢。

  2. Pingback引用通告: 43ytr.icu/j/GPoAr

  3. Pingback引用通告: Èãðà ïðåñòîëîâ 8 ñåçîí Ëîñòôèëüì

  4. Pingback引用通告: Èãðà ïðåñòîëîâ 8 ñåçîí

  5. Pingback引用通告: abisko.ru

  6. Pingback引用通告: glyxar.ru

  7. Pingback引用通告: 2021

  8. Pingback引用通告: ðîêåòìåí

  9. Pingback引用通告: wwin-tv.com

  10. Pingback引用通告: Video

  11. Pingback引用通告: Watch

  12. Pingback引用通告: watch videos

  13. Pingback引用通告: watch online

  14. Pingback引用通告: 00-tv.com

  15. Pingback引用通告: 4serial.com

  16. Pingback引用通告: kino

  17. Pingback引用通告: hs;br

  18. Pingback引用通告: tureckie_serialy_na_russkom_jazyke

  19. Pingback引用通告: tureckie_serialy

  20. Pingback引用通告: serialy

  21. Pingback引用通告: +1+

  22. Pingback引用通告: æóêè+2+ñåðèÿ

  23. Pingback引用通告: Ñìîòðåòü ñåðèàëû îíëàéí âñå ñåðèè ïîäðÿä

  24. Pingback引用通告: Ñìîòðåòü âñå ñåðèè ïîäðÿä

  25. Pingback引用通告: âûòîïêà âîñêà

  26. Pingback引用通告: ++++++

  27. Pingback引用通告: HD-720

  28. Pingback引用通告: guardians+of+the+galaxy+2

  29. Pingback引用通告: strong woman do bong soon

  30. Pingback引用通告: my id is gangnam beauty

  31. Pingback引用通告: guardians of the galaxy vol 2

  32. Pingback引用通告: 2020

  33. Pingback引用通告: kpop+star+season+6+ep+9

  34. Pingback引用通告: 1 2 3 4 5 6 7 8 9 10

  35. Pingback引用通告: Watch TV Shows

  36. Pingback引用通告: Kinokrad 2019 Kinokrad Hd

  37. Pingback引用通告: Kinokrad

  38. Pingback引用通告: filmy-kinokrad

  39. Pingback引用通告: kinokrad-2019

  40. Pingback引用通告: filmy-2019-kinokrad

  41. Pingback引用通告: serial

  42. Pingback引用通告: cerialest.ru

  43. Pingback引用通告: youtube2019.ru

  44. Pingback引用通告: dorama hdrezka

  45. Pingback引用通告: movies hdrezka

  46. Pingback引用通告: HDrezka

  47. Pingback引用通告: kinosmotretonline

  48. Pingback引用通告: LostFilm HD 720

  49. Pingback引用通告: trustedmdstorefy.com

  50. Pingback引用通告: bofilm ñåðèàë

  51. Pingback引用通告: bofilm

  52. Pingback引用通告: 1 seriya

  53. Pingback引用通告: Êîíñóëüòàöèÿ ïñèõîëîãà

  54. Pingback引用通告: topedstoreusa.com

  55. Pingback引用通告: hqcialismht.com

  56. Pingback引用通告: viagramdtrustser.com

  57. Pingback引用通告: rick and morty season 3

  58. Pingback引用通告: See-Season-1

  59. Pingback引用通告: Evil-Season-1

  60. Pingback引用通告: Evil-Season-2

  61. Pingback引用通告: Evil-Season-3

  62. Pingback引用通告: Evil-Season-4

  63. Pingback引用通告: Dollface-Season-1

  64. Pingback引用通告: Queer-Eye-We-re-in-Japan-Season-1

  65. Pingback引用通告: serial 2020

  66. Pingback引用通告: Dailymotion

  67. Pingback引用通告: Watch+movies+2020

  68. Pingback引用通告: serial-video-film-online

  69. Pingback引用通告: tvrv.ru

  70. Pingback引用通告: 1plus1serial.site

  71. Pingback引用通告: #1plus1

  72. Pingback引用通告: 1plus1

  73. Pingback引用通告: Watch Movies Online

  74. Pingback引用通告: Film

  75. Pingback引用通告: Film 2020

  76. Pingback引用通告: Film 2021

  77. Pingback引用通告: Top 10 Best

  78. Pingback引用通告: watch online TV LIVE

  79. Pingback引用通告: parazity-oskar-2020

  80. Pingback引用通告: human design

  81. Pingback引用通告: DSmlka

  82. Pingback引用通告: viagra

  83. Pingback引用通告: generic viagra

  84. Pingback引用通告: +

  85. Pingback引用通告: ¯jak Son³k

  86. Pingback引用通告: astrolog

  87. Pingback引用通告: film-kalashnikov-watch

  88. Pingback引用通告: generic cialis

  89. Pingback引用通告: cialis 20mg

  90. Pingback引用通告: kinoxaxru.ru

  91. Pingback引用通告: pobachennya u vegas

  92. Pingback引用通告: Proshanie so Stalinym

  93. Pingback引用通告: strelcov 2020

  94. Pingback引用通告: film t-34

  95. Pingback引用通告: online pharmacy

  96. Pingback引用通告: canadian pharmacy

  97. Pingback引用通告: Beograd film 2020

  98. Pingback引用通告: psiholog

  99. Pingback引用通告: psixolog

  100. Pingback引用通告: psyhelp_on_line

  101. Pingback引用通告: coronavirus

  102. Pingback引用通告: PSYCHOSOCIAL

  103. Pingback引用通告: rasstanovka hellinger

  104. Pingback引用通告: Cherekasi film 2020

  105. Pingback引用通告: film doktor_liza

  106. Pingback引用通告: djoker film

  107. Pingback引用通告: t.me/psyhell

  108. Pingback引用通告: Ïñèõîëîã îíëàéí

  109. Pingback引用通告: bitly.com

  110. Pingback引用通告: viagra 100mg

  111. Pingback引用通告: viagra price

  112. Pingback引用通告: viagra generic

  113. Pingback引用通告: viagra coupon

  114. Pingback引用通告: cheap viagra

  115. Pingback引用通告: cialis

  116. Pingback引用通告: cialis coupon

  117. Pingback引用通告: canadian pharmacy cialis

  118. Pingback引用通告: cialis 5mg

  119. Pingback引用通告: rlowcostmd.com

  120. Pingback引用通告: bitly

  121. Pingback引用通告: movies-tekstmovies-tekst

  122. Pingback引用通告: Zemlyane 2005 smotret onlajn

  123. Pingback引用通告: smotret onlajn besplatno v kachestve hd 1080

  124. Pingback引用通告: gusmeasu.com

  125. Pingback引用通告: movies-unhinged-film

  126. Pingback引用通告: malenkie-zhenshhiny-2020

  127. Pingback引用通告: dom 2

  128. Pingback引用通告: zoom-psykholog

  129. Pingback引用通告: zoom-viber-skype

  130. Pingback引用通告: Vratar Galaktiki Film, 2020

  131. Pingback引用通告: Vratar

  132. Pingback引用通告: Cherkassy 2020

  133. Pingback引用通告: chernobyl-hbo-2019-1-sezon

  134. Pingback引用通告: moskva-psiholog

  135. Pingback引用通告: batmanapollo.ru

  136. Pingback引用通告: 323

  137. Pingback引用通告: 525

  138. Pingback引用通告: dom2-ru

  139. Pingback引用通告: Tenet Online

  140. Pingback引用通告: psy psy psy psy

  141. Pingback引用通告: krsmi.ru

  142. Pingback引用通告: like-v.ru

  143. Pingback引用通告: CFOSPUK

  144. Pingback引用通告: MAMprEj

  145. Pingback引用通告: fgu0ygW

  146. Pingback引用通告: batmanapollo

  147. Pingback引用通告: tsoy

  148. Pingback引用通告: 44548

  149. Pingback引用通告: 44549

  150. Pingback引用通告: hod-korolevy-2020

  151. Pingback引用通告: HD

  152. Pingback引用通告: 158444

  153. Pingback引用通告: groznyy-serial-2020

  154. Pingback引用通告: 38QvPmk

  155. Pingback引用通告: bitly.com/doctor-strange-hd

  156. Pingback引用通告: bitly.com/eternals-online

  157. Pingback引用通告: bitly.com/maior-grom

  158. Pingback引用通告: matrica-film

  159. Pingback引用通告: dzhonuikfilm4

  160. Pingback引用通告: bitly.com/batman20212022

  161. Pingback引用通告: bitly.com/venom-2-smotret-onlajn

  162. Pingback引用通告: bitly.com/nevremyaumirat

  163. Pingback引用通告: bitly.com/kingsmankingsman

  164. Pingback引用通告: bitly.com/3zaklyatie3

  165. Pingback引用通告: bitly.com/1dreykfilm

  166. Pingback引用通告: bitly.com/topgunmavericktopgun

  167. Pingback引用通告: bitly.com/flash2022

  168. Pingback引用通告: bitly.com/fantasticheskietvari3

  169. Pingback引用通告: bitly.com/wonderwoman1984hd

  170. Pingback引用通告: 1444

  171. Pingback引用通告: cleantalkorg2.ru

  172. Pingback引用通告: 232dfsad

  173. Pingback引用通告: cleantalkorg2.ru/sitemap.xml

  174. Pingback引用通告: join vk

  175. Pingback引用通告: vk login

  176. Pingback引用通告: svaty—7—sezon

  177. Pingback引用通告: svaty 7 sezon

  178. Pingback引用通告: svaty 7

  179. Pingback引用通告: tik tok

  180. Pingback引用通告: 666

  181. Pingback引用通告: The Revenant

  182. Pingback引用通告: 2021

  183. Pingback引用通告: D4

  184. Pingback引用通告: 777

  185. Pingback引用通告: link

  186. Pingback引用通告: 4569987

  187. Pingback引用通告: news news news

  188. Pingback引用通告: psy

  189. Pingback引用通告: psy2022

  190. Pingback引用通告: projectio-freid

  191. Pingback引用通告: kinoteatrzarya.ru

  192. Pingback引用通告: topvideos

  193. Pingback引用通告: afisha-kinoteatrov.ru

  194. Pingback引用通告: Ukrainskie-serialy

  195. Pingback引用通告: site

  196. Pingback引用通告: top

  197. Pingback引用通告: soderzhanki-3-sezon-2021.online

评论已关闭。