Windows Forms: Eine Cursor-Bitmap teilweise transparent machen

Ich möchte teilweise transparente Bilder in Drag & Drop-Vorgängen verwenden. Dies ist alles eingerichtet und funktioniert einwandfrei, aber die eigentliche Umwandlung in Transparenz hat einen seltsamen Nebeneffekt. Aus irgendeinem Grund scheinen die Pixel vor einem schwarzen Hintergrund zu verschmelzen.

Das folgende Bild beschreibt das Problem:

Abbildung a) ist die Original-Bitmap.

Abbildung b) zeigt, was nach dem Alpha-Blending entsteht. Offensichtlich ist dies viel dunkler als der beabsichtigte 50% Alpha-Filter beabsichtigt.

Abbildung c) zeigt den gewünschten Effekt, Bild a) mit 50% Transparenz (hinzugefügt zur Komposition mit einem Zeichenprogramm).

Der Code, den ich verwende, um das transparente Bild zu erstellen, ist der folgende:

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;

Weiß jemand, warum das Alpha-Blending nicht funktioniert?

Update I:

Aus Gründen der Übersichtlichkeit funktioniert der Code, wenn ich auf einer gezeichneten Oberfläche alphabetisch schreibe. Das Problem ist, dass ich aus einem vorhandenen Bild ein vollständig semitransparentes Bild erstellen und dieses als dynamischen Cursor während Drag & Drop-Vorgängen verwenden möchte. Selbst wenn Sie das Obige überspringen und nur ein gefülltes Farbrechteck malen, erhalten Sie eine dunkelgraue Farbe. Mit der Ikone ist etwas faul.

Update II:

Da ich viel nachgeforscht habe und glaube, dass dies etwas mit der Cursor-Erstellung zu tun hat, werde ich diesen Code auch unten einfügen. Wenn ich die Bitmap direkt vor dem Aufruf von CreateIconIndirect mit GetPixel abtaste, scheinen die vier Farbwerte intakt zu sein. Daher habe ich das Gefühl, dass die Schuldigen die hbmColor- oder die hbmMask-Mitglieder der IconInfo-Struktur sein könnten.

Hier ist die IconInfo-Struktur:

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. 

}

Und hier ist der Code, der den Cursor tatsächlich erstellt:

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

Die zwei externen Funktionen sind wie folgt definiert:

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

Antworten auf die Frage(5)

Ihre Antwort auf die Frage