如何将标准 windows 信息图标很好地绘制到页面控件选项卡的索引

How to paint standard windows information icon nicely to the index of a pagecontrol's tab

我想要将标准 windows 信息(以及警告和错误)图标绘制到页面控件的选项卡索引中。但是,如果 windows 背景颜色不是白色,结果看起来很糟糕。

program Project111;

uses
  Vcl.Forms,
  Vcl.Controls,
  Vcl.Graphics,
  Winapi.Windows,
  Vcl.ComCtrls,
  Vcl.ImgList;

{$R *.res}

var
  mainForm: TForm;
  imageList: TImageList;
  icon: TIcon;
  pageControl: TPageControl;
  tabSheet: TTabSheet;
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;

  Application.CreateForm(TForm, mainForm);

  imageList := TImageList.Create(mainForm);
  imageList.ColorDepth := cd32bit;
  icon := TIcon.Create;
  try
    icon.Handle := LoadImage( 0, IDI_INFORMATION, IMAGE_ICON, 16, 16, {LR_DEFAULTSIZE or} LR_SHARED );
    imageList.AddIcon(icon);
  finally
    icon.Free;
  end;

  pageControl := TPageControl.Create(mainForm);
  pageControl.Parent := mainForm;
  pageControl.Images := imageList;

  tabSheet := TTabSheet.Create(mainForm);
  tabSheet.Parent := pageControl;
  tabSheet.PageControl := pageControl;
  tabSheet.ImageIndex := 0;

  Application.Run;
end.

截图如下:

如您所见,白色边框模糊不清,我猜这是因为 TImageList 缺少适当的 alpha 透明度,但我不知道如何解决这个问题。

该解决方案不必使用 TImageList,我很乐意使用任何其他方法。请注意,还会有标题,并非所有索引都有图标,并且图标可能 changed/added/removed 随着上下文的变化。

我正在使用 Delphi XE-2,如果有帮助,我也有 DevExpress 组件。

您看到的不是因为 alpha 透明度损坏,而是因为调整了大小。

documented 一样,LoadImage 加载的图标的默认大小为 SM_CXICONxSM_CYICON,通常为 32x32。由于您正在请求系统共享的图标,因此这将是将要提供的图标的大小。

您可以验证您的代码是否属于这种情况:

  ..
  try
    icon.Handle := LoadImage( 0, IDI_INFORMATION, IMAGE_ICON, 16, 16, {LR_DEFAULTSIZE or} LR_SHARED );
    Assert(icon.Width = GetSystemMetrics(SM_CXICON));
    Assert(GetSystemMetrics(SM_CXICON) <> 16);
    ..

接下来是调整图标大小以适合图像列表,可能没有什么比 StretchBltCOLORONCOLOR 模式更好。

不幸的是,无法加载非标准尺寸的系统图标,因为再次记录,

When loading a system icon or cursor, you must use LR_SHARED or the function will fail to load the resource.


您需要做的是以默认大小加载共享图标,然后使用更好的算法自行将其调整为 16x16。 StretchBltHALFTONE 可能会更好,但使用更高级的图形库可能会产生最佳效果。

强烈建议不要使用的替代方法是直接从资源所在的位置加载资源。如果您不使用 LR_SHARED 标志,您将获得请求的大小。你需要做一些研究来找到图标的实际索引,因为这没有记录。并且需要考虑索引或它所在的位置可能会随时间发生变化。在这种情况下不要忘记自己销毁图标,因为系统不会为非共享资源这样做。

icon.Handle := LoadImage(GetModuleHandle(user32), Pointer(104), IMAGE_ICON, 16, 16, 0);

正如@Sertac 所说,您看到的是将 windows shell 图标从 32x32 调整为 16x16 的效果,作为从 Windows Vista 开始的解决方法,您可以使用 SHGetStockIconInfo 函数。 传递 SHGSI_SMALLICON 标志以检索由 SM_CXSMICONSM_CYSMICON.

指定的小版本图标

SM_CXSMICONSM_CYSMICON 的值取决于当前的 DPI 设置。对于 DPI 96 是 16x16.

样本

  LIcon := TIcon.Create;
  try
    LIcon.Handle := 0;
    if TOSVersion.Check(6, 0) then
    begin
      ZeroMemory(@LSHStockIconInfo, SizeOf(LSHStockIconInfo));
      LSHStockIconInfo.cbSize := sizeof(LSHStockIconInfo);
      if SHGetStockIconInfo(SIID_INFO, SHGSI_ICON or SHGSI_SMALLICON, LSHStockIconInfo) = S_OK then
      begin
        LIcon.Handle := LSHStockIconInfo.hIcon;
        imageList.AddIcon(LIcon);
      end;
    end;
  finally
    LIcon.Free;
  end;