问题:尝试使用Paint事件中的转换从(或在)鼠标位置缩放(缩放)图像,将位图原点转换为鼠标位置,然后缩放图像,然后再转换原点。转换鼠标位置时,图像“跳跃”并且无法从重定位的原点缩放。正确旋转,缩放和平移功能而无需转换为鼠标位置。相关代码块如下。
[在Win10 1909 v18363.778中使用Visual Studio运行C#Winforms .Net 4.7.2
private void trackBar1_Scroll(object sender, EventArgs e)
{
// Get rotation angle
ang = trackBar1.Value;
pnl1.Invalidate();
}
private void pnl1_MouseWheel(object sender, MouseEventArgs e)
{
// Get mouse location
mouse = e.location;
// Get new scale (zoom) factor
zoom = (float)(e.Delta > 0 ? zoom * 1.05 : zoom / 1.05);
pnl1.Invalidate();
}
private void pnl1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
pan = true;
mouX = e.X;
mouY = e.Y;
oldX = imgX;
oldY = imgY;
}
private void pnl1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || !pan) return;
// Coordinates of panned image
imgX = oldX + e.X - mouX;
imgY = oldY + e.Y - mouY;
pnl1.Invalidate();
}
private void pnl1_MouseUp(object sender, MouseEventArgs e)
{
pan = false;
}
private void pnl1_Paint(object sender, PaintEventArgs e)
{
// Apply rotation angle @ center of bitmap
e.Graphics.TranslateTransform(img.Width / 2, img.Height / 2);
e.Graphics.RotateTransform(ang);
e.Graphics.TranslateTransform(-img.Width / 2, -img.Height / 2);
// Apply scaling factor - focused @ mouse location
e.Graphics.TranslateTransform(mouse.X, mouse.Y, MatrixOrder.Append);
e.Graphics.ScaleTransform(zoom, zoom, MatrixOrder.Append);
e.Graphics.TranslateTransform(-mouse.X, -mouse.Y, MatrixOrder.Append);
// Apply drag (pan) location
e.Graphics.TranslateTransform(imgX, imgY, MatrixOrder.Append);
// Draw "bmp" @ location
e.Graphics.DrawImage(img, 0, 0);
}
一些建议和几个技巧。不完全是技巧,只有一些方法可以在进行多个图形转换时加快计算速度。
该示例代码显示了4种缩放方法,并生成了旋转转换(并排运行,请勿accumulate)。使用枚举器(private enum ZoomMode
)选择缩放模式:
缩放模式:
ImageLocation
:就地进行图像缩放,将画布上的当前位置保持在固定位置。 CenterCanvas
:在缩放图像时,它仍在画布上居中。 CenterMouse
:缩放并转换图像以使其自身在画布上当前鼠标位置的中心。 MouseOffset
:缩放并平移图像,以维持由鼠标指针在图像本身上的初始位置确定的相对位置。 您会注意到,该代码简化了所有计算,仅相对于定义当前Image边界的Rectangle且仅相对于此Shape的位置应用平移。仅在计算需要优先确定图像大小为[[after时,鼠标滚轮已生成下一个缩放因子时,才缩放矩形。
已实现功能的可视示例
:样本代码
:canvas
trkRotationAngle
radZoom_CheckedChanged
Tag
属性中分配。将这些控件添加到设计器中的“窗体”中。 using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
public partial class frmZoomPaint : Form
{
private float rotationAngle = 0.0f;
private float zoomFactor = 1.0f;
private float zoomStep = .05f;
private RectangleF imageRect = RectangleF.Empty;
private PointF imageLocation = PointF.Empty;
private PointF mouseLocation = PointF.Empty;
private Bitmap drawingImage = null;
private PictureBoxEx canvas = null;
private ZoomMode zoomMode = ZoomMode.ImageLocation;
private enum ZoomMode
{
ImageLocation,
CenterCanvas,
CenterMouse,
MouseOffset
}
public frmZoomPaint()
{
InitializeComponent();
string imagePath = [Path of the Image];
drawingImage = (Bitmap)Image.FromStream(new MemoryStream(File.ReadAllBytes(imagePath)));
imageRect = new RectangleF(Point.Empty, drawingImage.Size);
canvas = new PictureBoxEx(new Size(555, 300));
canvas.Location = new Point(10, 10);
canvas.MouseWheel += this.canvas_MouseWheel;
canvas.MouseMove += this.canvas_MouseMove;
canvas.MouseDown += this.canvas_MouseDown;
canvas.MouseUp += this.canvas_MouseUp;
canvas.Paint += this.canvas_Paint;
this.Controls.Add(canvas);
}
private void canvas_MouseWheel(object sender, MouseEventArgs e)
{
mouseLocation = e.Location;
float zoomCurrent = zoomFactor;
zoomFactor += e.Delta > 0 ? zoomStep : -zoomStep;
if (zoomFactor < .10f) zoomStep = .01f;
if (zoomFactor >= .10f) zoomStep = .05f;
if (zoomFactor < .0f) zoomFactor = zoomStep;
switch (zoomMode) {
case ZoomMode.CenterCanvas:
imageRect = CenterScaledRectangleOnCanvas(imageRect, canvas.ClientRectangle);
break;
case ZoomMode.CenterMouse:
imageRect = CenterScaledRectangleOnMousePosition(imageRect, e.Location);
break;
case ZoomMode.MouseOffset:
imageRect = OffsetScaledRectangleOnMousePosition(imageRect, zoomCurrent, e.Location);
break;
default:
break;
}
canvas.Invalidate();
}
private void canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
mouseLocation = e.Location;
imageLocation = imageRect.Location;
canvas.Cursor = Cursors.NoMove2D;
}
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
imageRect.Location =
new PointF(imageLocation.X + (e.Location.X - mouseLocation.X),
imageLocation.Y + (e.Location.Y - mouseLocation.Y));
canvas.Invalidate();
}
private void canvas_MouseUp(object sender, MouseEventArgs e) =>
canvas.Cursor = Cursors.Default;
private void canvas_Paint(object sender, PaintEventArgs e)
{
var drawingRect = GetDrawingImageRect(imageRect);
using (var mxRotation = new Matrix())
using (var mxTransform = new Matrix()) {
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
mxRotation.RotateAt(rotationAngle, GetDrawingImageCenterPoint(drawingRect));
mxTransform.Multiply(mxRotation);
e.Graphics.Transform = mxTransform;
e.Graphics.DrawImage(drawingImage, drawingRect);
}
}
private void trkRotationAngle_ValueChanged(object sender, EventArgs e)
{
rotationAngle = trkAngle.Value;
canvas.Invalidate();
canvas.Focus();
}
private void radZoom_CheckedChanged(object sender, EventArgs e)
{
var rad = sender as RadioButton;
if (rad.Checked) {
zoomMode = (ZoomMode)int.Parse(rad.Tag.ToString());
}
canvas.Focus();
}
#region Drawing Methods
public RectangleF GetScaledRect(RectangleF rect, float scaleFactor) =>
new RectangleF(rect.Location,
new SizeF(rect.Width * scaleFactor, rect.Height * scaleFactor));
public RectangleF GetDrawingImageRect(RectangleF rect) =>
GetScaledRect(rect, zoomFactor);
public PointF GetDrawingImageCenterPoint(RectangleF rect) =>
new PointF(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
public RectangleF CenterScaledRectangleOnCanvas(RectangleF rect, RectangleF canvas)
{
var scaled = GetScaledRect(rect, zoomFactor);
rect.Location = new PointF((canvas.Width - scaled.Width) / 2,
(canvas.Height - scaled.Height) / 2);
return rect;
}
public RectangleF CenterScaledRectangleOnMousePosition(RectangleF rect, PointF mousePosition)
{
var scaled = GetScaledRect(rect, zoomFactor);
rect.Location = new PointF(mousePosition.X - (scaled.Width / 2),
mousePosition.Y - (scaled.Height / 2));
return rect;
}
public RectangleF OffsetScaledRectangleOnMousePosition(RectangleF rect, float currentZoom, PointF mousePosition)
{
var currentRect = GetScaledRect(imageRect, currentZoom);
if (!currentRect.Contains(mousePosition)) return rect;
float scaleRatio = currentRect.Width / GetScaledRect(rect, zoomFactor).Width;
PointF mouseOffset = new PointF(mousePosition.X - rect.X, mousePosition.Y - rect.Y);
PointF scaledOffset = new PointF(mouseOffset.X / scaleRatio, mouseOffset.Y / scaleRatio);
PointF position = new PointF(rect.X - (scaledOffset.X - mouseOffset.X),
rect.Y - (scaledOffset.Y - mouseOffset.Y));
rect.Location = position;
return rect;
}
#endregion
}
简单的PictureBoxEx
自定义控件(根据需要进行修改和扩展):此PictureBox是可选的,因此可以通过单击鼠标将其聚焦
using System.ComponentModel; using System.Drawing; using System.Windows.Forms; [DesignerCategory("Code")] public class PictureBoxEx : PictureBox { public PictureBoxEx() : this (new Size(200, 200)){ } public PictureBoxEx(Size size) { SetStyle(ControlStyles.Selectable | ControlStyles.UserMouse, true); this.BorderStyle = BorderStyle.FixedSingle; this.Size = size; } }