Windows 窗体:使光标位图部分透明
我想在拖/放操作中使用部分透明的图像。 这一切都已设置并且工作正常,但实际的透明转换会产生奇怪的副作用。 由于某种原因,像素似乎与黑色背景混合在一起。
下图描述了该问题:
图 a) 是原始位图。
图 b) 是执行 alpha 混合后生成的结果。 显然,这比预期的 50% Alpha 滤镜暗很多。
图 c) 是所需的效果,图像 a) 具有 50% 透明度(使用绘图程序添加到合成中)。
我用来生成透明图像的代码如下:
Bitmap bmpNew = new Bitmap(bmpOriginal.Width, bmpOriginal.Height);
Graphics g = Graphics.FromImage(bmpNew);
// Making the bitmap 50% transparent:
float[][] ptsArray ={
new float[] {1, 0, 0, 0, 0}, // Red
new float[] {0, 1, 0, 0, 0}, // Green
new float[] {0, 0, 1, 0, 0}, // Blue
new float[] {0, 0, 0, 0.5f, 0}, // Alpha
new float[] {0, 0, 0, 0, 1} // Brightness
};
ColorMatrix clrMatrix = new ColorMatrix(ptsArray);
ImageAttributes imgAttributes = new ImageAttributes();
imgAttributes.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(bmpOriginal, new Rectangle(0, 0, bmpOriginal.Width, bmpOriginal.Height), 0, 0, bmpOriginal.Width, bmpOriginal.Height, GraphicsUnit.Pixel, imgAttributes);
Cursors.Default.Draw(g, new Rectangle(bmpOriginal.Width / 2 - 8, bmpOriginal.Height / 2 - 8, 32, 32));
g.Dispose();
imgAttributes.Dispose();
return bmpNew;
有谁知道为什么 alpha 混合不起作用?
更新一:
为了清楚起见,如果我在绘制的表面上进行字母混合,代码确实可以工作。 问题是我想从现有图像创建一个完全半透明的图像,并在拖/放操作期间将其用作动态光标。 即使跳过上述步骤并仅绘制颜色 88ffffff 的填充矩形也会产生深灰色。 该图标有些可疑。
更新二:
由于我已经研究了很多并且相信这与光标的创建有关,所以我也将在下面包含该代码。 如果我在 CreateIconIndirect 调用之前对位图进行 GetPixel 采样,则四个颜色值似乎完好无损。 因此,我有一种感觉,罪魁祸首可能是 IconInfo 结构的 hbmColor 或 hbmMask 成员。
这是 IconInfo 结构:
public struct IconInfo { // http://msdn.microsoft.com/en-us/library/ms648052(VS.85).aspx
public bool fIcon; // Icon or cursor. True = Icon, False = Cursor
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask; // Specifies the icon bitmask bitmap. If this structure defines a black and white icon,
// this bitmask is formatted so that the upper half is the icon AND bitmask and the lower
// half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two.
// If this structure defines a color icon, this mask only defines the AND bitmask of the icon.
public IntPtr hbmColor; // Handle to the icon color bitmap. This member can be optional if this structure defines a black
// and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination;
// subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag.
}
这是实际创建 Cursor 的代码:
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot) {
IconInfo iconInfo = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref iconInfo);
iconInfo.hbmColor = (IntPtr)0;
iconInfo.hbmMask = bmp.GetHbitmap();
iconInfo.xHotspot = xHotSpot;
iconInfo.yHotspot = yHotSpot;
iconInfo.fIcon = false;
return new Cursor(CreateIconIndirect(ref iconInfo));
}
两个外部函数定义如下:
[DllImport("user32.dll", EntryPoint = "CreateIconIndirect")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
I want to use partially transparent images in drag/drop operations. This is all set up and works fine, but the actual transformation to transparency has a weird side effect. For some reason, the pixels seem to be blended against a black background.
The following image describes the problem:
Figure a) is the original bitmap.
Figure b) is what is produced after alpha blending has been performed. Obviously this is a lot darker than the intended 50% alpha filter intended.
Figure c) is the desired effect, image a) with 50% transparency (added to the composition with a drawing program).
The code I use to produce the trasparent image is the following:
Bitmap bmpNew = new Bitmap(bmpOriginal.Width, bmpOriginal.Height);
Graphics g = Graphics.FromImage(bmpNew);
// Making the bitmap 50% transparent:
float[][] ptsArray ={
new float[] {1, 0, 0, 0, 0}, // Red
new float[] {0, 1, 0, 0, 0}, // Green
new float[] {0, 0, 1, 0, 0}, // Blue
new float[] {0, 0, 0, 0.5f, 0}, // Alpha
new float[] {0, 0, 0, 0, 1} // Brightness
};
ColorMatrix clrMatrix = new ColorMatrix(ptsArray);
ImageAttributes imgAttributes = new ImageAttributes();
imgAttributes.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(bmpOriginal, new Rectangle(0, 0, bmpOriginal.Width, bmpOriginal.Height), 0, 0, bmpOriginal.Width, bmpOriginal.Height, GraphicsUnit.Pixel, imgAttributes);
Cursors.Default.Draw(g, new Rectangle(bmpOriginal.Width / 2 - 8, bmpOriginal.Height / 2 - 8, 32, 32));
g.Dispose();
imgAttributes.Dispose();
return bmpNew;
Does anyone know why the alpha blending does not work?
Update I:
For clarity, the code does work if I'm alphablending on top of a drawn surface. The problem is that I want to create a completely semitransparent image from an existing image and use this as a dynamic cursor during drag/drop operations. Even skipping the above and only painting a filled rectangle of color 88ffffff yields a dark grey color. Something fishy is going on with the icon.
Update II:
Since I've reseached a whole lot and believe this has got something to do with the Cursor creation, I'm gonna include that code below too. If I GetPixel-sample the bitmap just before the CreateIconIndirect call, the four color values seem to be intact. Thus I have a feeling the culprits might be the hbmColor or the hbmMask members of the IconInfo structure.
Here's the IconInfo structure:
public struct IconInfo { // http://msdn.microsoft.com/en-us/library/ms648052(VS.85).aspx
public bool fIcon; // Icon or cursor. True = Icon, False = Cursor
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask; // Specifies the icon bitmask bitmap. If this structure defines a black and white icon,
// this bitmask is formatted so that the upper half is the icon AND bitmask and the lower
// half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two.
// If this structure defines a color icon, this mask only defines the AND bitmask of the icon.
public IntPtr hbmColor; // Handle to the icon color bitmap. This member can be optional if this structure defines a black
// and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination;
// subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag.
}
And here is the code that actually creates the Cursor:
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot) {
IconInfo iconInfo = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref iconInfo);
iconInfo.hbmColor = (IntPtr)0;
iconInfo.hbmMask = bmp.GetHbitmap();
iconInfo.xHotspot = xHotSpot;
iconInfo.yHotspot = yHotSpot;
iconInfo.fIcon = false;
return new Cursor(CreateIconIndirect(ref iconInfo));
}
The two external functions are defined as follows:
[DllImport("user32.dll", EntryPoint = "CreateIconIndirect")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在与 GDI(和 Win32)进行互操作时,GDI+ 存在许多与 alpha 混合相关的问题。 在这种情况下,调用 bmp.GetHbitmap() 会将图像与黑色背景混合。 CodeProject 上的文章提供了有关该问题的更多详细信息以及所使用的解决方案用于将图像添加到图像列表。
您应该能够使用类似的代码来获取用于掩码的 HBITMAP:
GDI+ has a number of problems related to alpha blending when doing interop with GDI (and Win32). In this case, the call to bmp.GetHbitmap() will blend your image with a black background. An article on CodeProject gives more detail on the problem, and a solution that was used for adding images to an image list.
You should be able to use similar code to get the HBITMAP to use for the mask:
不久前,我读到这个问题是由于位图中预乘 Alpha 通道的要求而引起的。 我不确定这是否是 Windows 游标或 GDI 的问题,并且在我的一生中,我找不到与此相关的文档。 因此,虽然这个解释可能正确也可能不正确,但以下代码确实可以实现您想要的功能,即在光标位图中使用预乘的 Alpha 通道。
pinvoke 声明位于External 类中,如下所示:
关于代码的一些注释:
A while ago, I read this problem arises out of a requirement for pre-multiplied alpha channels in the bitmaps. I'm not sure if this was an issue with Windows cursors or GDI, and for the life of me, I cannot find documentation regarding this. So, while this explanation may or may not be correct, the following code does indeed do what you want, using a pre-multiplied alpha channel in the cursor bitmap.
The pinvoke declarations are found in the External class, shown here:
A few notes on the code:
尝试将蓝色的值降低到 0.7 或 0.6,看看这是否更接近您想要的值。
这是一个很好的网站,解释了 ColorMatrix:
try lowering Blue's value to .7 or .6 and see if that is closer to what you want.
Here's a good site that explains ColorMatrix:
当我运行您的代码来修改具有背景网格图像的图片框中的图像时,我无需更改代码即可获得您想要的效果。 也许您的图像被绘制在深色物体的顶部......
When I run your code to modify an image in a picturebox with a background grid image, I get the effect you desired without changing your code. Perhaps your image is being drawn over the top of something that has a dark color...