问题:尝试使用 Paint 事件中的转换从(或在)鼠标位置缩放(缩放)图像以将位图原点转换为鼠标位置,然后缩放图像并将其原点转换回来。
Running on .Net 4.7.2, using Visual Studio in Windows 10 1909 v18363.778
相关代码块:
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);
}
最佳答案
一些建议和一些技巧。
不完全是技巧,只是在多个图形转换到位时加快计算速度的一些方法。
在这里,只有 Matrix.RotateAt和 Matrix.Multiply被使用。
这里有一些关于矩阵变换的注释:Flip the GraphicsPath
这里还有一些注意事项:How to apply a fade transition effect to Images
示例代码显示了 4 种缩放方法,以及生成旋转变换(并排工作,不累积)。
使用枚举器(
private enum ZoomMode
)选择缩放模式:缩放模式 :
ImageLocation
:图像缩放就地执行,将 Canvas 上的当前位置保持在固定位置。 CenterCanvas
:当图像被缩放时,它保持在 Canvas 的中心。 CenterMouse
:图像被缩放并平移到 Canvas 上当前鼠标位置的中心。 MouseOffset
:图像被缩放和平移以保持由鼠标指针在图像本身上的初始位置确定的相对位置。 您会注意到,代码简化了所有计算,仅应用相对于定义当前图像边界的 Rectangle 且仅与此形状的位置相关的平移。
Rectangle 仅在计算需要预先确定 Image 大小为 时才缩放。之后 鼠标滚轮已生成下一个缩放因子。
已实现功能的视觉示例 :
示例代码 :
canvas
是自定义控件,派生自 PictureBox(您可以在底部找到它的定义)。此控件在代码中添加到表单,here。根据需要进行修改。 trkRotationAngle
是用来定义Image当前旋转的TrackBar。将此控件添加到设计器中的窗体。 radZoom_CheckedChanged
是用于设置当前缩放模式的所有 RadioButtons 的事件处理程序。这些控件设置的值在它们的Tag
中分配。属性(property)。将这些控件添加到设计器中的窗体。 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 += canvas_MouseWheel;
canvas.MouseMove += canvas_MouseMove;
canvas.MouseDown += canvas_MouseDown;
canvas.MouseUp += canvas_MouseUp;
canvas.Paint += canvas_Paint;
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);
BorderStyle = BorderStyle.FixedSingle;
Size = size;
}
}
关于c# - 从鼠标位置缩放和翻译图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61924051/