C#从鼠标位置缩放图像

问题描述 投票:0回答:1

问题:尝试使用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);
    }
c# image winforms transform scale
1个回答
0
投票

一些建议和几个技巧。不完全是技巧,只有一些方法可以在进行多个图形转换时加快计算速度。

  1. Divide and conquer:将不同的图形效果和转换拆分为可做一件事情的不同专用方法。然后以一种使该方法在需要时可以一起工作的方式进行设计。
  2. 保持简单:当Graphics对象需要累积多个转换时,矩阵的堆叠顺序可能导致误解。事先计算一些通用转换(大部分是平移和缩放),然后让GDI +渲染已经pre-cooked的对象和形状,比较简单(并且不易产生weird结果)。
  3. 使用正确的工具:例如,用作canvas的面板并非最佳选择。此控件不是双缓冲的;可以启用此功能,但Panel并非用于绘图,而PictureBox(或平面Label)则单独支持它。

该示例代码显示了4种缩放方法,并生成了旋转转换(并排运行,请勿accumulate)。使用枚举器(private enum ZoomMode)选择缩放模式:

缩放模式

  • ImageLocation就地进行图像缩放,将画布上的当前位置保持在固定位置。
  • CenterCanvas:在缩放图像时,它仍在画布上居中。
  • [CenterMouse:缩放并转换图像以使其自身在画布上当前鼠标位置的中心。
  • [MouseOffset:缩放并平移图像,以维持由鼠标指针在图像本身上的初始位置确定的相对位置。

您会注意到,该代码简化了所有计算,仅相对于定义当前Image边界的Rectangle且仅相对于此Shape的位置应用平移。仅在计算需要优先确定图像大小为[[after时,鼠标滚轮已生成下一个缩放因子时,才缩放矩形。

已实现功能的可视示例

GDI+ Zoom and Rotations Samples

样本代码

  • canvas
是自定义控件,从PictureBox派生(您可以在底部找到其定义)。此控件通过代码添加到窗体中。根据需要进行修改。
  • trkRotationAngle
  • 是用于定义图像当前旋转的TrackBar。将此控件添加到设计器中的窗体。
  • [radZoom_CheckedChanged
  • 是用于设置当前缩放模式的所有RadioButton的事件处理程序。这些控件集的值在其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; } }
    © www.soinside.com 2019 - 2024. All rights reserved.