将图像加载到MenuItem上会使预乘的alpha图像失去透明度

tes*_*est 0 c# winapi alpha contextmenu gimp

我真的需要一些帮助.我正在尝试将一个我认为32bpp的图像加载到MenuItem上的预乘alpha (我按照本指南在GIMP中制作图像).我知道ContextMenuStrip类,不想使用它.

以下是我用于将图像设置到MenuItem上的代码:

// apis
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetMenuItemInfo(IntPtr hMenu, uint uItem, bool fByPosition,
                                   [In] ref MENUITEMINFO lpmii);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr LoadImage(IntPtr hinst, string lpszName, uint uType,
                               int cxDesired, int cyDesired, uint fuLoad);

// structures
[StructLayout(LayoutKind.Sequential)]
struct MENUITEMINFO
{
    public uint cbSize;
    public uint fMask;
    public uint fType;
    public uint fState;
    public uint wID;
    public IntPtr hSubMenu;
    public IntPtr hbmpChecked;
    public IntPtr hbmpUnchecked;
    public IntPtr dwItemData;
    public string dwTypeData;
    public uint cch;
    public IntPtr hbmpItem;
}

// constants
private const uint LR_LOADFROMFILE = 0x10u;
private const uint IMAGE_BITMAP = 0x0u;
private const uint MIIM_BITMAP = 0x80u;

// points the to the image below in the preview of GIMP
private const string IMAGE_PATH = @"C:\Test\Images\premultalpha.bmp";

// methods
private void SetMenuItemImage()
{

    // get the hbitmap for the image
    // i am assuming that the alpha channel is preservered on this call
    IntPtr hbitmap = LoadImage(IntPtr.Zero, IMAGE_PATH, 
                               IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

    // create the menuiteminfo structure
    MENUITEMINFO mii = new MENUITEMINFO();

    mii.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));

    // retrieves or sets the hbmpItem member
    mii.fMask = MIIM_BITMAP;

    // handle to the bitmap displayed
    mii.hbmpItem = hbitmap;

    // returns true
    SetMenuItemInfo(this.ContextMenu1.Handle, 0, true, ref mii);
}
Run Code Online (Sandbox Code Playgroud)

这是使用我的图像的代码的结果:

代码结果

这里显而易见的问题是没有透明度,而是有黑色背景.

这是GIMP中的图像在遵循指南之前在保存和重新打开之前制作预乘Alpha通道的情况:

之前

这是保存和重新打开 GIMP中的图像:

后

我注意到我再也看不到图片之前版本的alpha通道掩码了.我不确定它是否与我在尝试将之前的图片保存为.bmp时获得的此消息有关:

花边

对不起,这是一个很长的帖子,但我想尽我所能提供所有信息.我不确定我的问题是关于MenuItem的透明度.我被告知如果你加载一个32bpp和预乘alpha的位图,透明度就可以了.

我知道我不能使用托管方法,Bitmap.Gethbitmap()因为它丢失了alpha通道.这就是为什么我改为使用LoadImagewinapi来保留它.

任何帮助是极大的赞赏.

Leo*_*son 6

如果将LR_CREATEDIBSECTION标志添加到LoadImage调用的最后一个参数,它将按原样加载BMP资源,包括任何alpha通道(如果它是32位BMP文件).当图像转换为兼容的位图时,做其他任何事情似乎都会失去alpha通道(即使桌面设置为32bpp).

如果BMP文件本身是正确的,那么当使用MIIM_BITMAP和hbmpItem分配给菜单项时,DIB似乎工作正常.

当启用主题时,这适用于Vista到Windows 8.1.

禁用主题时,这并不总是有效.(请注意,即使在Windows 8中,即使用户无法再在系统范围内禁用主题,也可以禁用主题.)

(有时它也适用于主题已禁用,老实说我不能肯定为什么.它在我的主要Win7x64机器上有和没有主题,但在运行Win7x64的测试虚拟机中,当主题被禁用时看起来很糟糕.我怀疑它取决于哪些其他shell扩展添加了菜单,并且如果第三方扩展恰好使用所有者绘制图标,它将shell翻转到不同的代码路径,这使得新的hbmpItem方法也可以通过如果没有预防,它在默认情况下无法正常工作.但这只是猜测.不一致似乎是Windows中的一个错误,并且不太可能被修复,因为它存在于Vista到Windows 8.1你只需要避免在禁用主题时使用它,即使在Windows 7或8上也是如此.)

无论是否启用主题,这在Windows XP上都无法正常工作.

在XP和更新版本的Windows上禁用主题,结果可能很丑:

  • 图标最终向右移动,与菜单上的大多数其他图标位于不同的位置,并且由于菜单行高度较小,因此16x16图标看起来很好并且与主题菜单中的其他图标一致将会太大在一个未经授权的菜单中.

  • 当提供32bpp HBITMAP时,XP也会忽略alpha通道,因此您将获得一个带有纯黑色背景的图标.

  • 因此,如果您关心这些情况,那么您必须回退并且不在旧的或未经授权的系统上提供图标,或提供替代图标.(将其设置为13x13,并在运行时自行将其与系统菜单颜色混合.)您还需要使用hbmpUnchecked字段而不是hbmpItem,以便图标显示在所需位置.

  • 奖励:好像所有这些都不够糟糕,主题检测API是一个混乱,可能会告诉你系统范围的主题,但不是关于应用程序是否已禁用主题本身或已表现为使用旧的comctl32 .dll文件.除了测试Windows XP之外,这似乎是一个很好的检查:

    (IsThemeActive() && IsAppThemed() && (GetThemeAppProperties() & STAP_ALLOW_CONTROLS))

    您可能还想使用comctl32.dll DllGetVersion导出来确定应用程序是否使用comctl32.dll版本6或更高版本(这意味着支持主题但不会告诉您它们是否已启用或禁用),但我是不确定是否需要.

回到启用主题的主要情况,我们不在XP上:

  • 如果您在菜单中看到一些透明度但有奇怪的点和其他工件等问题,或者当您将鼠标移到菜单项上时,点会消失并且图标背景变为白色,则表示Alpha通道不正确.它需要预乘alpha,预乘黑色.

我没有安装GIMP,所以我无法帮助,但这里是使用Photoshop创建BMP文件的步骤,以防他们帮助某人.

在Photoshop中:

  • 将源图像视为PNG.
  • 在"频道"选项卡上,添加一个新图层(它应自动称为"Alpha").
  • 用黑色填充整个Alpha通道.
  • 返回"图层"选项卡,然后ctrl单击主图层(它应该是到目前为止唯一的图层)以选择其透明度蒙版.
  • 返回"频道"选项卡,然后单击Alpha频道.您在上一步中所做的选择仍应处于活动状态; 用白色填充选择.
  • (此时,您有一个带有显式Alpha通道的非预乘的alpha图像.)
  • 现在返回"图层"选项卡,然后返回主图层后面的新图层.用黑色填充.
  • 最后,将文件另存为32-bpp的BMP.(确保在Photoshop的"另存为"对话框中勾选Alpha通道复选框.)

在添加背景图层之前制作的Alpha通道与黑色背景图层的组合产生预乘alpha,与黑色预乘.