• ICON图标文件解析


    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

  • 相关阅读:
    uniApp 实现微信小程序和app视频播放flv格式视频监控
    uniapp 给子组件传值不及时显示
    uni-app 中$refs 在app中无法使用
    使用甘特图
    背景图片加蒙版,里面内容不受影响
    MyBatis 多对一操作
    在Maven项目中使用lombok
    MyBatis使用分页
    Log4j打印日志
    paramiko 下载文件
  • 原文地址:https://www.cnblogs.com/findumars/p/5011850.html
Copyright © 2020-2023  润新知