|
|
@@ -1,12 +1,17 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Drawing;
|
|
|
+using System.Drawing.Imaging;
|
|
|
+using System.IO;
|
|
|
using Avalonia.Input;
|
|
|
using Avalonia.Platform;
|
|
|
using Avalonia.Win32.Interop;
|
|
|
+using SdBitmap = System.Drawing.Bitmap;
|
|
|
+using SdPixelFormat = System.Drawing.Imaging.PixelFormat;
|
|
|
|
|
|
namespace Avalonia.Win32
|
|
|
{
|
|
|
- internal class CursorFactory : IStandardCursorFactory
|
|
|
+ internal class CursorFactory : ICursorFactory
|
|
|
{
|
|
|
public static CursorFactory Instance { get; } = new CursorFactory();
|
|
|
|
|
|
@@ -29,8 +34,7 @@ namespace Avalonia.Win32
|
|
|
IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id));
|
|
|
if (cursor != IntPtr.Zero)
|
|
|
{
|
|
|
- PlatformHandle phCursor = new PlatformHandle(cursor, PlatformConstants.CursorHandleType);
|
|
|
- Cache.Add(cursorType, phCursor);
|
|
|
+ Cache.Add(cursorType, new CursorImpl(cursor, false));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -70,22 +74,119 @@ namespace Avalonia.Win32
|
|
|
{StandardCursorType.DragLink, 32516},
|
|
|
};
|
|
|
|
|
|
- private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =
|
|
|
- new Dictionary<StandardCursorType, IPlatformHandle>();
|
|
|
+ private static readonly Dictionary<StandardCursorType, CursorImpl> Cache =
|
|
|
+ new Dictionary<StandardCursorType, CursorImpl>();
|
|
|
|
|
|
- public IPlatformHandle GetCursor(StandardCursorType cursorType)
|
|
|
+ public ICursorImpl GetCursor(StandardCursorType cursorType)
|
|
|
{
|
|
|
- IPlatformHandle rv;
|
|
|
- if (!Cache.TryGetValue(cursorType, out rv))
|
|
|
+ if (!Cache.TryGetValue(cursorType, out var rv))
|
|
|
{
|
|
|
- Cache[cursorType] =
|
|
|
- rv =
|
|
|
- new PlatformHandle(
|
|
|
- UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
|
|
|
- PlatformConstants.CursorHandleType);
|
|
|
+ rv = new CursorImpl(
|
|
|
+ UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
|
|
|
+ false);
|
|
|
+ Cache.Add(cursorType, rv);
|
|
|
}
|
|
|
|
|
|
return rv;
|
|
|
}
|
|
|
+
|
|
|
+ public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
|
|
|
+ {
|
|
|
+ using var source = LoadSystemDrawingBitmap(cursor);
|
|
|
+ using var mask = AlphaToMask(source);
|
|
|
+
|
|
|
+ var info = new UnmanagedMethods.ICONINFO
|
|
|
+ {
|
|
|
+ IsIcon = false,
|
|
|
+ xHotspot = hotSpot.X,
|
|
|
+ yHotspot = hotSpot.Y,
|
|
|
+ MaskBitmap = mask.GetHbitmap(),
|
|
|
+ ColorBitmap = source.GetHbitmap(),
|
|
|
+ };
|
|
|
+
|
|
|
+ return new CursorImpl(UnmanagedMethods.CreateIconIndirect(ref info), true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private SdBitmap LoadSystemDrawingBitmap(IBitmapImpl bitmap)
|
|
|
+ {
|
|
|
+ using var memoryStream = new MemoryStream();
|
|
|
+ bitmap.Save(memoryStream);
|
|
|
+ return new SdBitmap(memoryStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ private unsafe SdBitmap AlphaToMask(SdBitmap source)
|
|
|
+ {
|
|
|
+ var dest = new SdBitmap(source.Width, source.Height, SdPixelFormat.Format1bppIndexed);
|
|
|
+
|
|
|
+ if (source.PixelFormat == SdPixelFormat.Format32bppPArgb)
|
|
|
+ {
|
|
|
+ throw new NotSupportedException(
|
|
|
+ "Images with premultiplied alpha not yet supported as cursor images.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (source.PixelFormat != SdPixelFormat.Format32bppArgb)
|
|
|
+ {
|
|
|
+ return dest;
|
|
|
+ }
|
|
|
+
|
|
|
+ var sourceData = source.LockBits(
|
|
|
+ new Rectangle(default, source.Size),
|
|
|
+ ImageLockMode.ReadOnly,
|
|
|
+ SdPixelFormat.Format32bppArgb);
|
|
|
+ var destData = dest.LockBits(
|
|
|
+ new Rectangle(default, source.Size),
|
|
|
+ ImageLockMode.ReadOnly,
|
|
|
+ SdPixelFormat.Format1bppIndexed);
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var pSource = (byte*)sourceData.Scan0.ToPointer();
|
|
|
+ var pDest = (byte*)destData.Scan0.ToPointer();
|
|
|
+
|
|
|
+ for (var y = 0; y < dest.Height; ++y)
|
|
|
+ {
|
|
|
+ for (var x = 0; x < dest.Width; ++x)
|
|
|
+ {
|
|
|
+ if (pSource[x * 4] == 0)
|
|
|
+ {
|
|
|
+ pDest[x / 8] |= (byte)(1 << (x % 8));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pSource += sourceData.Stride;
|
|
|
+ pDest += destData.Stride;
|
|
|
+ }
|
|
|
+
|
|
|
+ return dest;
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ source.UnlockBits(sourceData);
|
|
|
+ dest.UnlockBits(destData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class CursorImpl : ICursorImpl, IPlatformHandle
|
|
|
+ {
|
|
|
+ private readonly bool _isCustom;
|
|
|
+
|
|
|
+ public CursorImpl(IntPtr handle, bool isCustom)
|
|
|
+ {
|
|
|
+ Handle = handle;
|
|
|
+ _isCustom = isCustom;
|
|
|
+ }
|
|
|
+
|
|
|
+ public IntPtr Handle { get; private set; }
|
|
|
+ public string HandleDescriptor => PlatformConstants.CursorHandleType;
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ if (_isCustom && Handle != IntPtr.Zero)
|
|
|
+ {
|
|
|
+ UnmanagedMethods.DestroyIcon(Handle);
|
|
|
+ Handle = IntPtr.Zero;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|