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