Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
254 views
in Technique[技术] by (71.8m points)

c# - Windows Forms: Making a cursor bitmap partially transparent

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:

Transparency 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);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...