1、主要是实现对WPF中的光标进行旋转
2、代码如下
//https://stackoverflow.com/questions/18351872/how-to-convert-system-windows-input-cursor-to-imagesource
//https://wpf.2000things.com/2012/12/17/713-setting-the-cursor-to-an-image-of-an-uielement-while-dragging/
public class CursorHelper
{
private static class NativeMethods
{
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
public static extern bool DestroyIcon(IntPtr hIcon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeIconHandle()
: base(true)
{
}
override protected bool ReleaseHandle()
{
return NativeMethods.DestroyIcon(handle);
}
}
public static Cursor BitmapToCursor(System.Drawing.Bitmap bmp)
{
var iconInfo = new NativeMethods.IconInfo();
NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo);
iconInfo.xHotspot = 0;
iconInfo.yHotspot = 0;
iconInfo.fIcon = false;
SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo);
return CursorInteropHelper.Create(cursorHandle);
}
public static Cursor CreateCursor(UIElement element)
{
element.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(new System.Windows.Point(), element.DesiredSize));
RenderTargetBitmap rtb =
new RenderTargetBitmap(
(int)element.DesiredSize.Width,
(int)element.DesiredSize.Height,
96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
using (var bmp = new System.Drawing.Bitmap(ms))
{
return BitmapToCursor(bmp);
}
}
}
/// <summary>
/// Cursor转位图
/// </summary>
/// <param name="cur"></param>
/// <returns></returns>
public static Bitmap CursorToBitmap(System.Windows.Forms.Cursor cur)
{
//System.Drawing.Icon i = System.Drawing.Icon.ExtractAssociatedIcon(@"C:\Cursors\arrow.cur");
//System.Drawing.Bitmap b = i.ToBitmap();
var icon = System.Drawing.Icon.FromHandle(cur.Handle);
System.Drawing.Bitmap bIcon = icon.ToBitmap();
return bIcon;
}
/// <summary>
/// Cursor转位图
/// </summary>
/// <param name="cur"></param>
/// <returns></returns>
public static Bitmap BitmapFromCursor(System.Windows.Forms.Cursor cur)
{
var iconInfo = new NativeMethods.IconInfo();
NativeMethods.GetIconInfo(cur.Handle, ref iconInfo);
Bitmap bmp = Bitmap.FromHbitmap(iconInfo.hbmColor);
NativeMethods.DeleteObject(iconInfo.hbmColor);
NativeMethods.DeleteObject(iconInfo.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, System.Drawing.Imaging.PixelFormat.Format32bppArgb, bmData.Scan0);
bmp.UnlockBits(bmData);
return new Bitmap(dstBitmap);
}
/// <summary>
/// 旋转位图
/// 使用GDI+
/// </summary>
/// <param name="bm"></param>
/// <param name="angle"></param>
/// <returns></returns>
/// https://blog.csdn.net/lhtzbj12/article/details/54099572
public static Bitmap RotateBitmap(Bitmap bm, float angle)
{
https://docs.microsoft.com/zh-tw/dotnet/api/system.drawing.image.rotateflip?view=net-5.0
// 不能任意角度旋转
// bm.RotateFlip(RotateFlipType.Rotate180FlipY);
// return bm;
angle = angle % 360;
//弧度转换
double radian = angle * Math.PI / 180.0;
double cos = Math.Cos(radian);
double sin = Math.Sin(radian);
//原图的宽和高
int w = bm.Width;
int h = bm.Height;
int W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));
int H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));
//create a new empty bitmap to hold rotated image
Bitmap returnBitmap = new Bitmap(W, H);
//returnBitmap.SetResolution(bm.HorizontalResolution, bm.VerticalResolution);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(returnBitmap);
//计算偏移量
System.Drawing.Point Offset = new System.Drawing.Point((W - w) / 2, (H - h) / 2);
//构造图像显示区域:让图像的中心与窗口的中心点一致
System.Drawing.Point center = new System.Drawing.Point(W / 2, H / 2);
//g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
//move rotation point to center of image
g.TranslateTransform(center.X, center.Y);
//rotate
g.RotateTransform((float)angle);
//move image back
g.TranslateTransform(-center.X, -center.Y);
//draw passed in image onto graphics object
g.DrawImage(bm, new System.Drawing.Point(Offset.X, Offset.Y));
g.Dispose();
return returnBitmap;
}
/// <summary>
/// 旋转位图
/// </summary>
/// <param name="bmp"></param>
/// <param name="angle"></param>
/// <returns></returns>
/// https://stackoverflow.com/questions/12024406/how-can-i-rotate-an-image-by-any-degree
public static Bitmap RotateImg(Bitmap bmp, float angle)
{
int w = bmp.Width;
int h = bmp.Height;
System.Drawing.Imaging.PixelFormat pf = bmp.PixelFormat;
//System.Drawing.Color bkColor = System.Drawing.Color.Transparent;
GraphicsPath path = new GraphicsPath();
path.AddRectangle(new RectangleF(0f, 0f, w, h));
System.Drawing.Drawing2D.Matrix mtrx = new System.Drawing.Drawing2D.Matrix();
//Using System.Drawing.Drawing2D.Matrix class
mtrx.Rotate(angle);
RectangleF rct = path.GetBounds(mtrx);
Bitmap newImg = new Bitmap(Convert.ToInt32(rct.Width), Convert.ToInt32(rct.Height), pf);
Graphics g = Graphics.FromImage(newImg);
g.Clear(System.Drawing.Color.LightBlue);
g.TranslateTransform(-rct.X, -rct.Y);
g.RotateTransform(angle);
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImageUnscaled(bmp, 0, 0);
g.Dispose();
return newImg;
}
/// <summary>
/// 旋转位图
/// </summary>
/// <param name="bm"></param>
/// <param name="angle"></param>
/// <returns></returns>
/// http://csharphelper.com/blog/2016/03/rotate-images-in-c/
public static Bitmap RotateBitmapByPath(Bitmap bm, float angle)
{
// Make a Matrix to represent rotation by this angle.
System.Drawing.Drawing2D.Matrix rotate_at_origin = new System.Drawing.Drawing2D.Matrix();
rotate_at_origin.Rotate(angle);
// Rotate the image's corners to see how big
// it will be after rotation.
PointF[] points =
{
new PointF(0, 0),
new PointF(bm.Width, 0),
new PointF(bm.Width, bm.Height),
new PointF(0, bm.Height),
};
rotate_at_origin.TransformPoints(points);
float xmin, xmax, ymin, ymax;
GetPointBounds(points, out xmin, out xmax, out ymin, out ymax);
// Make a bitmap to hold the rotated result.
int wid = (int)Math.Round(xmax - xmin);
int hgt = (int)Math.Round(ymax - ymin);
Bitmap result = new Bitmap(wid, hgt);
// Create the real rotation transformation.
System.Drawing.Drawing2D.Matrix rotate_at_center = new System.Drawing.Drawing2D.Matrix();
rotate_at_center.RotateAt(angle,
new PointF(wid / 2f, hgt / 2f));
// Draw the image onto the new bitmap rotated.
using (Graphics gr = Graphics.FromImage(result))
{
// Use smooth image interpolation.
gr.InterpolationMode = InterpolationMode.High;
// Clear with the color in the image's upper left corner.
//gr.Clear(bm.GetPixel(0, 0));
For debugging. (Makes it easier to see the background.)
// gr.Clear(System.Drawing.Color.LightBlue);
// Set up the transformation to rotate.
gr.Transform = rotate_at_center;
// Draw the image centered on the bitmap.
int x = (wid - bm.Width) / 2;
int y = (hgt - bm.Height) / 2;
gr.DrawImage(bm, x, y);
}
// Return the result bitmap.
return result;
}
/// <summary>
/// Find the bounding rectangle for an array of points.
/// </summary>
/// <param name="points"></param>
/// <param name="xmin"></param>
/// <param name="xmax"></param>
/// <param name="ymin"></param>
/// <param name="ymax"></param>
private static void GetPointBounds(PointF[] points, out float xmin, out float xmax, out float ymin, out float ymax)
{
xmin = points[0].X;
xmax = xmin;
ymin = points[0].Y;
ymax = ymin;
foreach (PointF point in points)
{
if (xmin > point.X) xmin = point.X;
if (xmax < point.X) xmax = point.X;
if (ymin > point.Y) ymin = point.Y;
if (ymax < point.Y) ymax = point.Y;
}
}
/// <summary>
/// 使用WPF变换位图
/// </summary>
/// <param name="bm"></param>
/// <param name="angle"></param>
/// <returns></returns>
/// https://stackoverflow.com/questions/6084585/wpf-drawingvisual-on-a-background-thread
/// https://social.msdn.microsoft.com/Forums/vstudio/en-US/b1a2c1f4-fa1f-440a-ba5b-ea9d2fe3a38f/background-image-and-color-setting-in-canvas?forum=wpf
public static Bitmap TransformBitmap(Bitmap bm, float angle)
{
angle = angle % 360;
//弧度转换
double radian = angle * Math.PI / 180.0;
double cos = Math.Cos(radian);
double sin = Math.Sin(radian);
//原图的宽和高
int w = bm.Width;
int h = bm.Height;
int W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));
int H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));
// Create DrawingVisual and get its drawing context
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
BitmapImage bitmap = new BitmapImage();
using (MemoryStream ms = new MemoryStream())
{
// 注意需要使用ImageFormat.Png或ImageFormat.Jpeg
// 如果使用Image.Bmp,则生成的位图背景为黑色
bm.Save(ms, ImageFormat.Png);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = ms;
bitmap.EndInit();
// 平移变换
dc.PushTransform(new TranslateTransform((W - w) / 2, (H - h) / 2));
// 旋转变换
dc.PushTransform(new RotateTransform(angle, bm.Width / 2, bm.Height / 2));
dc.DrawImage(bitmap, new Rect(0, 0, bm.Width, bm.Height));
dc.Pop();
dc.Pop();
}
// 必须先关闭,如果在return resultBmp前一句位置处关闭
// 则出现的是全黑的位图
dc.Close();
var renderBitmap = new RenderTargetBitmap(bitmap.PixelWidth, bitmap.PixelHeight, bitmap.DpiX, bitmap.DpiY, PixelFormats.Pbgra32);
renderBitmap.Render(dv);
System.Drawing.Bitmap resultBmp = null;
using (MemoryStream outStream = new MemoryStream())
{
// 注意使用PngBitmapEncoder或JpegBitmapEncoder
// 使用BmpBitmapEncoder生成的位图变成了黑色
BitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(renderBitmap));
enc.Save(outStream);
resultBmp = new System.Drawing.Bitmap(outStream);
}
//using (FileStream fi = new FileStream("c:\\test.bmp", FileMode.Create))
//{
// BitmapEncoder enc = new PngBitmapEncoder();
// enc.Frames.Add(BitmapFrame.Create(renderBitmap));
// enc.Save(fi);
//}
return resultBmp;
}
}
3、调用如下:
RotateCursor(System.Windows.Forms.Cursors.SizeNS);
/// <summary>
/// 旋转光标
/// </summary>
/// <param name="cursor"></param>
/// <returns></returns>
private Cursor RotateCursor(System.Windows.Forms.Cursor cursor)
{
var bm = CursorHelper.BitmapFromCursor(cursor);
//bm = CursorHelper.RotateBitmapByPath(bm, (float)this.rotateAngle);
bm = CursorHelper.TransformBitmap(bm, (float)this.rotateAngle);
return CursorHelper.BitmapToCursor(bm);
}