Windows窗体:使光标位图部分透明 [英] Windows Forms: Making a cursor bitmap partially transparent

查看:337
本文介绍了Windows窗体:使光标位图部分透明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在拖放操作中使用部分透明的图片。这是所有设置和工作正常,但实际转换到透明度有一个奇怪的副作用。由于某些原因,像素似乎与黑色背景混合。



以下图片描述了此问题:





图a)是原始位图。



图b)是在执行α混合之后产生的内容。显然这比预期的50%alpha滤镜暗得多。



图c)是所需的效果,图像a)具有50%的透明度用绘图程序)。



我用来产生trasparent图像的代码如下:

  Bitmap bmpNew = new Bitmap(bmpOriginal.Width,bmpOriginal.Height); 
Graphics g = Graphics.FromImage(bmpNew);

//使位图50%透明:
float [] [] ptsArray = {
new float [] {1,0,0,0,0} / Red
new float [] {0,0,0,0},// Green
new float [] {0,0,1,0,0} $ b new float [] {0,0,0,0.5f,0},// Alpha
new float [] {0,0,0,0,1} //亮度
}
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混合不起作用吗?



更新I:



为了清楚起见,如果我在绘制曲面的顶部问题是,我想从现有的图像创建一个完全半透明的图像,并在拖放操作过程中使用它作为一个动态光标。即使跳过上面,只画一个填充的颜色88ffffff矩形产生深灰色。


$ b

更新二:



我已经讨论了很多,并相信这与光标创建有关,我会在下面包括这段代码。如果我GetPixel样本位图刚好在CreateIconIndirect调用之前,四个颜色值似乎是完整的。因此,我有一个感觉,罪魁祸首可能是html5的IconInfo结构的hbmColor或hbmMask成员。



这里是IconInfo结构:

  public struct IconInfo {// http://msdn.microsoft.com/en-us/library/ms648052(VS.85).aspx 
public bool fIcon; //图标或光标。 True = Icon,False = Cursor
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask; //指定图标位掩码位图。如果这个结构定义了一个黑白图标,
//这个位掩码被格式化,使得上半部分是图标AND位掩码,而下
//一半是图标XOR位掩码。在这种情况下,高度应该是2的偶数倍。
//如果此结构定义了颜色图标,此掩码仅定义图标的AND位掩码。
public IntPtr hbmColor; //处理图标颜色位图。如果此结构定义了一个黑色
//和白色图标,则此成员可以是可选的。 hbmMask的AND位掩码将SRCAND标志应用于目标;
//接下来,通过使用SRCINVERT标志将彩色位图应用(使用XOR)到目标。

}

这里是实际创建Cursor的代码: / p>

  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);


解决方案

GDI +有许多与alpha混合相关的问题与GDI(和Win32)进行互操作。在这种情况下,调用bmp.GetHbitmap()会将您的图像与黑色背景混合。关于CodeProject 的文章提供了有关该问题的更多详细信息,以及使用的解决方案



您应该能够使用类似的代码来获取HBITMAP用于掩码:

  [DllImport(kernel32.dll)] 
public static extern bool RtlMoveMemory(IntPtr dest,IntPtr source,int dwcount)
[DllImport(gdi32.dll)]
public static extern IntPtr CreateDIBSection(IntPtr hdc,[In,MarshalAs(UnmanagedType.LPStruct)] BITMAPINFO pbmi,uint iUsage,out IntPtr ppvBits,IntPtr hSection, uint dwOffset);

public static IntPtr GetBlendedHBitmap(Bitmap bitmap)
{
BITMAPINFO bitmapInfo = new BITMAPINFO();
bitmapInfo.biSize = 40;
bmapmapInfo.biBitCount = 32;
bitmapInfo.biPlanes = 1;

bitmapInfo.biWidth = bitmap.Width;
bitmapInfo.biHeight = -bitmap.Height;

IntPtr pixelData;
IntPtr hBitmap = CreateDIBSection(
IntPtr.Zero,bitmapInfo,0,out pixelData,IntPtr.Zero,0);

Rectangle bounds = new Rectangle(0,0,bitmap.Width,bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(
bounds,ImageLockMode.ReadOnly,PixelFormat.Format32bppArgb);
RtlMoveMemory(
pixelData,bitmapData.Scan0,bitmap.Height * bitmapData.Stride);

bitmap.UnlockBits(bitmapData);
return hBitmap;
}


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);

解决方案

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:

[DllImport("kernel32.dll")]
public static extern bool RtlMoveMemory(IntPtr dest, IntPtr source, int dwcount);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateDIBSection(IntPtr hdc, [In, MarshalAs(UnmanagedType.LPStruct)]BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

public static IntPtr GetBlendedHBitmap(Bitmap bitmap)
{
    BITMAPINFO bitmapInfo = new BITMAPINFO();
    bitmapInfo.biSize = 40;
    bitmapInfo.biBitCount = 32;
    bitmapInfo.biPlanes = 1;

    bitmapInfo.biWidth = bitmap.Width;
    bitmapInfo.biHeight = -bitmap.Height;

    IntPtr pixelData;
    IntPtr hBitmap = CreateDIBSection(
        IntPtr.Zero, bitmapInfo, 0, out pixelData, IntPtr.Zero, 0);

    Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    BitmapData bitmapData = bitmap.LockBits(
        bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );
    RtlMoveMemory(
        pixelData, bitmapData.Scan0, bitmap.Height * bitmapData.Stride);

    bitmap.UnlockBits(bitmapData);
    return hBitmap;
}

这篇关于Windows窗体:使光标位图部分透明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆