如何将透明光标渲染为位图并保留 Alpha 通道?

9
我使用以下代码来渲染透明图标:
    private void button1_Click(object sender, EventArgs e)
    {
        // using LoadCursorFromFile from user32.dll
        var cursor = NativeMethods.LoadCustomCursor(@"d:\Temp\Cursors\Cursors\aero_busy.ani");

        // cursor -> bitmap
        Bitmap bitmap = new Bitmap(48, 48, PixelFormat.Format32bppArgb);
        Graphics gBitmap = Graphics.FromImage(bitmap);
        cursor.DrawStretched(gBitmap, new Rectangle(0, 0, 32, 32));

        // 1. Draw bitmap on a form canvas
        Graphics gForm = Graphics.FromHwnd(this.Handle);
        gForm.DrawImage(bitmap, 50, 50);

        // 2. Draw cursor directly to a form canvas
        cursor.Draw(gForm, new Rectangle(100, 50, 32, 32));

        cursor.Dispose();
    }

很抱歉,我无法将透明光标渲染为位图!直接将光标绘制到窗体画布上可以工作,但将光标绘制到位图时就会出现问题。 非常感谢任何建议。

alt text


是的,GDI+ 在绘制图标时保留 alpha 通道方面存在已知问题。也许有人知道解决方法,但我认为最好的方法是改用 GDI。 - Cody Gray
嗯...真遗憾,因为我想继续使用托管代码。你知道Windows 7是否仍然使用GDI来渲染透明光标吗? - Murat from Daminion Software
2
看起来很熟悉。这里需要署名。如果你注意这一点,可能会得到一个更好的答案。 - Hans Passant
2个回答

9
您现在使用的解决方案不完全是托管代码。您自己的评论说明,您正在从user32.dll中调用LoadCursorFromFile。而且,使用Win32 API真的不是您应该害怕的事情。
正如我在评论中提到的,使用GDI+绘图函数(例如大多数由.NET Framework提供的函数)经常会出现问题。使用GDI可以使任务变得更加容易。您可以使用以下代码从光标(或图标,它们基本上是可互换的)创建位图,并确保它们遵循alpha通道:
[StructLayout(LayoutKind.Sequential)]    
private struct ICONINFO
{
    public bool fIcon;
    public int xHotspot;
    public int yHotspot;
    public IntPtr hbmMask;
    public IntPtr hbmColor;
}

[DllImport("user32")]
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO pIconInfo);

[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string lpFileName);

[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);

private Bitmap BitmapFromCursor(Cursor cur)
{
    ICONINFO ii;
    GetIconInfo(cur.Handle, out ii);

    Bitmap bmp = Bitmap.FromHbitmap(ii.hbmColor);
    DeleteObject(ii.hbmColor);
    DeleteObject(ii.hbmMask);

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);
    bmp.UnlockBits(bmData);

    return new Bitmap(dstBitmap);
}

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    //Using LoadCursorFromFile from user32.dll, get a handle to the icon
    IntPtr hCursor = LoadCursorFromFile("C:\\Windows\\Cursors\\Windows Aero\\aero_busy.ani");

    //Create a Cursor object from that handle
    Cursor cursor = new Cursor(hCursor);

    //Convert that cursor into a bitmap
    using (Bitmap cursorBitmap = BitmapFromCursor(cursor))
    {
        //Draw that cursor bitmap directly to the form canvas
        e.Graphics.DrawImage(cursorBitmap, 50, 50);
    }
}

如果此代码无法编译,请确保您已将System.DrawingSystem.Drawing.ImagingSystem.Runtime.InteropServices添加到using语句中。还要记得将Form1_Paint方法绑定为表单的Paint事件处理程序。

已测试可正常工作:

光标,从带alpha通道的位图绘制完成


你好,那里有一段很棒的代码。当我调用BitmapFromCursor时,在"Bitmap bmp = Bitmap.FromHbitmap(ii.hbmColor);"这一行会出现"A generic error occurred in GDI+."的错误提示。有人知道是什么原因导致的吗?该如何修复呢? - Neaox
好的,我已经找出了 ii.hbmColor 返回了空指针。有人知道可能是为什么,以及如何修复它吗? - Neaox
更多细节- 光标 IBeam 是问题所在,我应该怎么做来解决它? - Neaox
@Neaox 请看下面的答案! - SepehrM

3

@Cody Gray,这不适用于具有低色位数的光标。

另一种替代方法是使用 Icon.ExtractAssociatedIcon

System.Drawing.Icon i = System.Drawing.Icon.ExtractAssociatedIcon(@"C:\Windows\Cursors\arrow_rl.cur");
System.Drawing.Bitmap b = i.ToBitmap();

希望这能对某些人有所帮助……

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接