How do I create a dynamic mouse cursor .NET without using interop?

前端 未结 4 1672
一向
一向 2021-01-20 07:33

I have an application which I\'m interested in eventually porting to mono so I\'m trying to avoid using p/invoke\'s to accomplish this task.

I would like to load a c

相关标签:
4条回答
  • 2021-01-20 08:13

    The code below creates a stream filled with data in .cur format, which I succesfully tested in Ubuntu with Mono. However .NET without using Win32 API only supports black and white colors in custom cursor:

    The Cursor class does not support animated cursors (.ani files) or cursors with colors other than black and white.

    Therefore in Unix with Mono one is rewarded with a cursor which

    • displays black and white colors only
    • has no semi-transparent pixels, only fully opaque / fully transparent ones

    ¯\_(ツ)_/¯

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace StackoverflowExample
    {
        public static class CursorHelper
        {
            public static Cursor CreateCursor(Bitmap bmp, Point hotSpot)
            {
                // https://en.wikipedia.org/wiki/ICO_(file_format)
                var bmpData = bmp.LockBits(new Rectangle(default, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    
                try
                {
                    int numBytes = bmpData.Stride * bmpData.Height;
                    var bgraValues = new byte[numBytes];
                    Marshal.Copy(bmpData.Scan0, bgraValues, 0, numBytes);
    
                    int max = Math.Max(bmp.Width, bmp.Height);
    
                    if (max > 256)
                        throw new NotSupportedException();
    
                    byte iconSizeByte = _sizes.FirstOrDefault(s => s >= max); // 0 means 256
                    int iconSizeI = iconSizeByte == 0 ? 256 : iconSizeByte;
                    const int bytesPerPixel = 4;
                    const int bytesPerPixelSource = 4;
                    byte[] emptyPixel = new byte[bytesPerPixel];
    
                    using (var stream = new MemoryStream())
                    using (var writer = new BinaryWriter(stream))
                    {
                        writer.Write((ushort)0); // idReserved
                        writer.Write((ushort)2); // idType, 1 = .ico 2 = .cur
                        writer.Write((ushort)1); // idCount
    
                        writer.Write(iconSizeByte);
                        writer.Write(iconSizeByte);
                        writer.Write((byte)0); // colorCount
                        writer.Write((byte)0); // reserved
                        writer.Write((ushort)hotSpot.X);
                        writer.Write((ushort)hotSpot.Y);
    
                        var pixelsCount = iconSizeI * iconSizeI;
                        var xorLength = pixelsCount * bytesPerPixel;
                        var andLength = pixelsCount / 8 * 2;
    
                        writer.Write((uint)(40 + xorLength + andLength)); // sizeInBytes
                        writer.Write((uint)stream.Position + sizeof(uint)); // fileOffset = 22 = 0x16
    
                        writer.Write(40u); // cursorInfoHeader.biSize
                        writer.Write((int)iconSizeI); // cursorInfoHeader.biWidth
                        writer.Write((int)iconSizeI * 2); // cursorInfoHeader.biHeight
                        writer.Write((ushort)1); // cursorInfoHeader.biPlanes
                        writer.Write((ushort)(8 * bytesPerPixel)); // cursorInfoHeader.biBitCount
                        writer.Write(0u); // cursorInfoHeader.biCompression
                        writer.Write(0u); // cursorInfoHeader.biSizeImage
                        writer.Write(0); // cursorInfoHeader.biXPelsPerMeter;
                        writer.Write(0); // cursorInfoHeader.biYPelsPerMeter;
                        writer.Write(0u); // cursorInfoHeader.biClrUsed = binaryReader2.ReadUInt32();
                        writer.Write(0u); // cursorInfoHeader.biClrImportant = binaryReader2.ReadUInt32();
    
                        using (var andMask = new MemoryStream(andLength))
                        {
                            byte def = 255;
    
                            for (int j = 0; j < iconSizeI; j++)
                            {
                                int y = iconSizeI - 1 - j;
                                byte curByte = def;
    
                                for (int i = 0; i < iconSizeI; i++)
                                {
                                    var bitIndex = 7 - i % 8;
    
                                    if (i < bmp.Width && y < bmp.Height)
                                    {
                                        var p = y * bmpData.Stride + i * bytesPerPixelSource;
                                        stream.Write(bgraValues, p, bytesPerPixel);
    
                                        if (bgraValues[p + 3] > 0)
                                            curByte = (byte)(curByte & ~(1 << bitIndex));
                                    }
                                    else
                                        stream.Write(emptyPixel, 0, emptyPixel.Length);
    
                                    if (bitIndex == 0)
                                    {
                                        andMask.WriteByte(curByte);
                                        curByte = def;
                                    }
                                }
                            }
    
                            for (int j = 0; j < iconSizeI; j++)
                                for (int b = 0; b < iconSizeI / 8; b++)
                                    andMask.WriteByte(def);
    
                            andMask.Seek(0, SeekOrigin.Begin);
                            andMask.CopyTo(stream);
                        }
    
                        stream.Seek(0, SeekOrigin.Begin);
    
                        // debug
                        // File.WriteAllBytes("/home/kolia/Documents/stream", stream.ToArray());
                        // stream.Seek(0, SeekOrigin.Begin);
    
                        var cursor = new Cursor(stream);
                        return cursor;
                    }
                }
                finally
                {
                    bmp.UnlockBits(bmpData);
                }
            }
    
            private static readonly byte[] _sizes = { 16, 32, 64, 128 };
        }
    }
    
    0 讨论(0)
  • 2021-01-20 08:27

    I was reference the post: How can i replace cursor with bitmap in winform

    You can create a Array of static cursor and use Timer to change it

    to make dynamic mouse cursor effect!

    Create static cursor from bitmap is so simply and without using interop:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
    
            Icon icon = this.Icon;
            Bitmap bmp = icon.ToBitmap();
            Cursor cur = new Cursor(bmp.GetHicon());
    
            this.Cursor = cur;
        }
    }
    
    0 讨论(0)
  • 2021-01-20 08:31

    Here's an example in VB.NET of creating a bitmap on the fly and turning it into a cursor. Not sure who this will help 7+ years on, but here goes:

    Private Function GetCircleCursor(iDiameter As Integer) As Cursor
    Dim oBitmap As Bitmap = New Bitmap(Convert.ToInt32(iDiameter), Convert.ToInt32(iDiameter), System.Drawing.Imaging.PixelFormat.Format32bppArgb)
    
        Using g As System.Drawing.Graphics = Graphics.FromImage(oBitmap)
            g.Clear(Color.Transparent)
    
            g.DrawEllipse(New System.Drawing.Pen(Color.White, 3), New Rectangle(0, 0, iDiameter, iDiameter))
            g.DrawEllipse(New System.Drawing.Pen(Color.Black, 1), New Rectangle(0, 0, iDiameter, iDiameter))
        End Using
    
        Return New Cursor(oBitmap.GetHicon)
    End Function
    
    0 讨论(0)
  • 2021-01-20 08:32

    Simple: YOU CAN NOT - the functionaltiy you ask for is not part of the .NET framework, so you need to go native.

    If you application needds porting to mono, isloate this code in one class so you can turn if off like with a compiler switch - not hard.

    0 讨论(0)
提交回复
热议问题