Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows 如何从资源加载图标而不产生别名?_Windows_Delphi_Winapi - Fatal编程技术网

Windows 如何从资源加载图标而不产生别名?

Windows 如何从资源加载图标而不产生别名?,windows,delphi,winapi,Windows,Delphi,Winapi,我有一个GUI应用程序,其中包括许多用于工具栏按钮、菜单图示符、通知图标等的图标。这些图标作为资源和各种不同大小的可用图标链接到应用程序。通常,对于工具栏按钮图像,我有可用的16px、24px和32px版本。我的图标为32bpp,部分透明 该应用程序具有高DPI感知能力,并根据当前的字体缩放调整所有视觉元素的大小。例如,在100%字体缩放96dpi时,工具栏图标大小为16px。在125%缩放、120dpi时,工具栏图标大小为20px。我需要能够加载一个大小为20px的图标,没有任何锯齿效果。我该

我有一个GUI应用程序,其中包括许多用于工具栏按钮、菜单图示符、通知图标等的图标。这些图标作为资源和各种不同大小的可用图标链接到应用程序。通常,对于工具栏按钮图像,我有可用的16px、24px和32px版本。我的图标为32bpp,部分透明


该应用程序具有高DPI感知能力,并根据当前的字体缩放调整所有视觉元素的大小。例如,在100%字体缩放96dpi时,工具栏图标大小为16px。在125%缩放、120dpi时,工具栏图标大小为20px。我需要能够加载一个大小为20px的图标,没有任何锯齿效果。我该怎么做?请注意,我希望支持Windows 2000及更高版本。

在Vista及更高版本上添加了许多新功能,使此任务变得简单。这里最合适的功能是

此函数将首先在图标文件中搜索大小完全相同的图标。如果未找到匹配项,则除非cx和cy都匹配标准图标大小(16、32、48或256像素)中的一个,否则将选择下一个最大的图标,然后缩小到所需大小。例如,如果callign应用程序请求x维度为40像素的图标,则使用48像素图标并将其缩小到40像素。相反,LoadImage功能选择32像素图标并将其缩放至40像素

如果函数找不到较大的图标,则默认为查找下一个最小图标并将其放大到所需大小的标准行为

根据我的经验,这个函数在缩放方面做得很好,并且结果没有显示混叠的迹象

就我所知,对于早期版本的Windows,没有任何单一功能可以充分执行此任务。从
LoadImage
获得的结果质量非常差。相反,我发现最好的方法如下:

  • 检查资源中的可用图像,以查找最大尺寸小于所需图标尺寸的图像
  • 创建所需大小的新图标,并将其初始化为完全透明
  • 将资源中较小的图标放在新(较大)图标的中心
  • 这意味着图标周围将有一个小的透明边框,但通常该边框小到可以忽略不计。理想的选择是使用可按比例缩小的代码,就像
    LoadIconWithScaleDown
    所做的那样,但这对编写来说并不简单

    因此,这里是我使用的代码

    unit uLoadIconResource;
    
    interface
    
    uses
      SysUtils, Math, Classes, Windows, Graphics, CommCtrl;
    
    function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception
    function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON;
    
    implementation
    
    function IconSizeFromMetric(IconMetric: Integer): Integer;
    begin
      case IconMetric of
      ICON_SMALL:
        Result := GetSystemMetrics(SM_CXSMICON);
      ICON_BIG:
        Result := GetSystemMetrics(SM_CXICON);
      else
        raise EAssertionFailed.Create('Invalid IconMetric');
      end;
    end;
    
    procedure GetDIBheaderAndBits(bmp: HBITMAP; out bih: BITMAPINFOHEADER; out bits: Pointer);
    var
      pbih: ^BITMAPINFOHEADER;
      bihSize, bitsSize: DWORD;
    begin
      bits := nil;
      GetDIBSizes(bmp, bihSize, bitsSize);
      pbih := AllocMem(bihSize);
      Try
        bits := AllocMem(bitsSize);
        GetDIB(bmp, 0, pbih^, bits^);
        if pbih.biSize<SizeOf(bih) then begin
          FreeMem(bits);
          bits := nil;
          exit;
        end;
        bih := pbih^;
      Finally
        FreeMem(pbih);
      End;
    end;
    
    function CreateIconFromSmallerIcon(IconSize: Integer; SmallerIcon: HICON): HICON;
    
      procedure InitialiseBitmapInfoHeader(var bih: BITMAPINFOHEADER);
      begin
        bih.biSize := SizeOf(BITMAPINFOHEADER);
        bih.biWidth := IconSize;
        bih.biHeight := 2*IconSize;//height of xor bitmap plus height of and bitmap
        bih.biPlanes := 1;
        bih.biBitCount := 32;
        bih.biCompression := BI_RGB;
      end;
    
      procedure CreateXORbitmap(const sbih, dbih: BITMAPINFOHEADER; sptr, dptr: PDWORD);
      var
        line, xOffset, yOffset: Integer;
      begin
        xOffset := (IconSize-sbih.biWidth) div 2;
        yOffset := (IconSize-sbih.biHeight) div 2;
        inc(dptr, xOffset + IconSize*yOffset);
        for line := 0 to sbih.biHeight-1 do begin
          Move(sptr^, dptr^, sbih.biWidth*SizeOf(DWORD));
          inc(dptr, IconSize);//relies on the fact that no padding is needed for RGBA scanlines
          inc(sptr, sbih.biWidth);//likewise
        end;
      end;
    
    var
      SmallerIconInfo: TIconInfo;
      sBits, xorBits: PDWORD;
      xorScanSize, andScanSize: Integer;
      xorBitsSize, andBitsSize: Integer;
      sbih: BITMAPINFOHEADER;
      dbih: ^BITMAPINFOHEADER;
      resbitsSize: DWORD;
      resbits: Pointer;
    
    begin
      Result := 0;
      Try
        if not GetIconInfo(SmallerIcon, SmallerIconInfo) then begin
          exit;
        end;
        Try
          GetDIBheaderAndBits(SmallerIconInfo.hbmColor, sbih, Pointer(sBits));
          if Assigned(sBits) then begin
            Try
              if (sbih.biWidth>IconSize) or (sbih.biHeight>IconSize) or (sbih.biPlanes<>1) or (sbih.biBitCount<>32) then begin
                exit;
              end;
    
              xorScanSize := BytesPerScanline(IconSize, 32, 32);
              Assert(xorScanSize=SizeOf(DWORD)*IconSize);
              andScanSize := BytesPerScanline(IconSize, 1, 32);
              xorBitsSize := IconSize*xorScanSize;
              andBitsSize := IconSize*andScanSize;
              resbitsSize := SizeOf(BITMAPINFOHEADER) + xorBitsSize + andBitsSize;
              resbits := AllocMem(resbitsSize);//AllocMem zeroises the memory
              Try
                dbih := resbits;
                InitialiseBitmapInfoHeader(dbih^);
    
                xorBits := resbits;
                inc(PByte(xorBits), SizeOf(BITMAPINFOHEADER));
                CreateXORbitmap(sbih, dbih^, sBits, xorBits);
    
                //don't need to fill in the mask bitmap when using RGBA
                Result := CreateIconFromResourceEx(resbits, resbitsSize, True, $00030000, IconSize, IconSize, LR_DEFAULTCOLOR);
              Finally
                FreeMem(resbits);
              End;
            Finally
              FreeMem(sBits);
            End;
          end;
        Finally
          if SmallerIconInfo.hbmMask<>0 then begin
            DeleteObject(SmallerIconInfo.hbmMask);
          end;
          if SmallerIconInfo.hbmColor<>0 then begin
            DeleteObject(SmallerIconInfo.hbmColor);
          end;
        End;
      Finally
        DestroyIcon(SmallerIcon);
      End;
    end;
    
    function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception
    
      function LoadImage(IconSize: Integer): HICON;
      begin
        Result := Windows.LoadImage(HInstance, PChar(ResourceName), IMAGE_ICON, IconSize, IconSize, LR_DEFAULTCOLOR);
      end;
    
    type
      TGrpIconDir = packed record
        idReserved: Word;
        idType: Word;
        idCount: Word;
      end;
    
      TGrpIconDirEntry = packed record
        bWidth: Byte;
        bHeight: Byte;
        bColorCount: Byte;
        bReserved: Byte;
        wPlanes: Word;
        wBitCount: Word;
        dwBytesInRes: DWORD;
        wID: WORD;
      end;
    
    var
      i, BestAvailableIconSize, ThisSize: Integer;
      ResourceNameWide: WideString;
      Stream: TResourceStream;
      IconDir: TGrpIconDir;
      IconDirEntry: TGrpIconDirEntry;
    
    begin
      //LoadIconWithScaleDown does high quality scaling and so we simply use it if it's available
      ResourceNameWide := ResourceName;
      if Succeeded(LoadIconWithScaleDown(HInstance, PWideChar(ResourceNameWide), IconSize, IconSize, Result)) then begin
        exit;
      end;
    
      //XP: find the closest sized smaller icon and draw without stretching onto the centre of a canvas of the right size
      Try
        Stream := TResourceStream.Create(HInstance, ResourceName, RT_GROUP_ICON);
        Try
          Stream.Read(IconDir, SizeOf(IconDir));
          Assert(IconDir.idCount>0);
          BestAvailableIconSize := high(BestAvailableIconSize);
          for i := 0 to IconDir.idCount-1 do begin
            Stream.Read(IconDirEntry, SizeOf(IconDirEntry));
            Assert(IconDirEntry.bWidth=IconDirEntry.bHeight);
            ThisSize := IconDirEntry.bHeight;
            if ThisSize=0 then begin//indicates a 256px icon
              continue;
            end;
            if ThisSize=IconSize then begin
              //a perfect match, no need to continue
              Result := LoadImage(IconSize);
              exit;
            end else if ThisSize<IconSize then begin
              //we're looking for the closest sized smaller icon
              if BestAvailableIconSize<IconSize then begin
                //we've already found one smaller
                BestAvailableIconSize := Max(ThisSize, BestAvailableIconSize);
              end else begin
                //this is the first one that is smaller
                BestAvailableIconSize := ThisSize;
              end;
            end;
          end;
          if BestAvailableIconSize<IconSize then begin
            Result := CreateIconFromSmallerIcon(IconSize, LoadImage(BestAvailableIconSize));
            if Result<>0 then begin
              exit;
            end;
          end;
        Finally
          FreeAndNil(Stream);
        End;
      Except
        ;//swallow because this routine is contracted not to throw exceptions
      End;
    
      //final fallback: make do without
      Result := 0;
    end;
    
    function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON;
    begin
      Result := LoadIconResourceSize(ResourceName, IconSizeFromMetric(IconMetric));
    end;
    
    end.
    
    单位资源;
    接口
    使用
    系统、数学、类、窗口、图形、CommCtrl;
    函数LoadIconResourceSize(常量ResourceName:string;IconSize:Integer):HICON//不会抛出异常
    函数LoadIconResourceMetric(常量ResourceName:string;IconMetric:Integer):HICON;
    实施
    函数IconSizeFromMetric(IconMetric:Integer):整数;
    开始
    非对称的
    小图标:
    结果:=GetSystemMetrics(SM_CXSMICON);
    大图标:
    结果:=GetSystemMetrics(SM_CXICON);
    其他的
    raise EASSERIONFAILED.Create('Invalid IconMetric');
    结束;
    结束;
    过程GetDIBheaderAndBits(bmp:HBITMAP;out bih:BitMapInfo头;out bits:Pointer);
    变量
    pbih:^BitMapInfo标头;
    比西泽,比西泽:德沃德;
    开始
    位:=零;
    getDibSize(bmp、bihSize、bitsize);
    pbih:=AllocMem(比西泽);
    尝试
    位:=AllocMem(位大小);
    GetDIB(bmp,0,pbih^,bits^);
    如果pbih.BISIZECONSIZE)或(sbih.biHeight>IconSize)或(sbih.biPlanes1)或(sbih.BIBIBITCUNT32),则开始
    出口
    结束;
    xorScanSize:=字节数perscanline(IconSize,32,32);
    断言(xorScanSize=SizeOf(DWORD)*IconSize);
    andScanSize:=字节扫描线(IconSize,1,32);
    xorBitsSize:=IconSize*xorScanSize;
    ANDBITSIZE:=IconSize*andScanSize;
    resbitsize:=SizeOf(BitMapInfo头)+xorBitsSize+和bitsize;
    resbits:=AllocMem(resbitsize)//AllocMem将内存归零
    尝试
    dbih:=resbits;
    initialiseBitMapInfo头文件(dbih^);
    xorBits:=resbits;
    inc(PByte(xorBits),SizeOf(bitmapinfo头));
    创建XorbitMap(sbih,dbih^,sBits,xorBits);
    //使用RGBA时不需要填充遮罩位图
    结果:=CreateIconFromResourceEx(resbits,resbitsize,True,$00030000,IconSize,IconSize,LR_DEFAULTCOLOR);
    最后
    FreeMem(resbits);
    结束;
    最后
    FreeMem(sBits);
    结束;
    结束;
    最后
    如果SmallerIconInfo.hbmMask0,则开始
    DeleteObject(SmallerIconInfo.hbmMask);
    结束;
    如果SmallerIconInfo.hbmColor0,则开始
    DeleteObject(SmallerIconInfo.hbmColor);
    结束;
    结束;
    最后
    销毁图标(SmallerIcon);
    结束;
    结束;
    函数LoadIconResourceSize(常量ResourceName:string;IconSize:Integer):HICON//不会抛出异常
    函数LoadImage(IconSize:Integer):HICON;
    开始
    结果:=Windows.LoadImage(HInstance,PChar(ResourceName),IMAGE_图标,IconSize,IconSize,LR_DEFAULTCOLOR);
    结束;
    类型
    TGrpIconDir=打包记录
    i保存:单词;
    idType:Word;
    idCount:Word;
    结束;
    TGrpIconDirEntry=打包记录
    bWidth:字节;
    bHeight:字节;
    b颜色计数:字节;
    b:字节;
    wPlanes:单词;
    wBitCount:单词;
    德比泰辛:德沃德;
    wID:单词;
    结束;
    变量
    i、 BestAvailableConsize,此大小:整数;
    ResourceNameWide:WideString;
    Stream:TResourceStream;
    IconDir:TGrpIconDir;
    IconDirEntry:TGrpIconDirEntry;
    开始
    //LoadIconWithScaleDown具有高质量的缩放功能,因此如果可用,我们只需使用它
    ResourceNameWide:=ResourceName;
    如果成功(LoadIconWithScaleDown(HInstance,PWideChar(资源