C# 从鼠标位置缩放和平移图像

C# 从鼠标位置缩放和平移图像,c#,image,winforms,graphics,gdi+,C#,Image,Winforms,Graphics,Gdi+,问题:尝试在绘制事件中使用变换从(或在)鼠标位置缩放(缩放)图像以将位图原点转换为鼠标位置,然后缩放图像并将其原点转换回 平移鼠标位置时,图像从重新定位的原点跳转并无法缩放 旋转、缩放和平移功能正常,无需平移到鼠标位置 在.NET4.7.2上运行,在Windows101909中使用VisualStudio v18363.778 相关代码块: private void trackBar1_Scroll(object sender, EventArgs e) { // Get rotat

问题:尝试在绘制事件中使用变换从(或在)鼠标位置缩放(缩放)图像以将位图原点转换为鼠标位置,然后缩放图像并将其原点转换回

  • 平移鼠标位置时,图像从重新定位的原点跳转并无法缩放
  • 旋转、缩放和平移功能正常,无需平移到鼠标位置
在.NET4.7.2上运行,在Windows101909中使用VisualStudio 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);
}

一些建议和一些技巧。
不完全是技巧,只是一些方法,以加快计算时,一个以上的图形转换是到位的

  • 分而治之:用不同的、专门的方法分割不同的图形效果和转换,这些方法只做一件事。然后进行设计,使这些方法在需要时能够协同工作

  • 保持简单:当图形对象需要累积多个变换时,矩阵的堆叠顺序可能会导致误解。事先计算一些通用变换(主要是平移和缩放),然后让GDI+渲染已经预处理好的对象和形状,这样更简单(也不容易产生奇怪的结果)。
    此处仅使用和。
    这里有一些关于矩阵变换的注释:

  • 使用正确的工具:例如,用作画布的面板并非最佳选择。此控件不是双缓冲的;可以启用此功能,但Panel类不用于绘图,而PictureBox(或非系统平面标签)本身支持此功能。
    这里还有一些注意事项:

  • 示例代码显示了4种缩放方法,并生成旋转变换(并排工作,不累加)。
    使用枚举器选择缩放模式(
    专用枚举ZoomMode
    ):

    缩放模式

    • 图像位置
      :图像缩放在适当位置执行,将画布上的当前位置保持在固定位置
    • CenterCanvas
      :缩放图像时,图像仍在画布上居中
    • CenterMouse
      :缩放图像并将其转换为画布上当前鼠标位置的中心
    • MouseOffset
      :缩放和平移图像,以保持由鼠标指针在图像本身上的初始位置确定的相对位置
    您可以注意到,该代码简化了所有计算,只应用相对于定义当前图像边界的矩形的平移,并且仅与此形状的位置相关。
    只有当计算需要在鼠标滚轮生成下一个缩放因子后预先确定图像大小时,才会缩放矩形

    已实施功能的可视示例

    示例代码

    • canvas
      是自定义控件,源自PictureBox(您可以在底部找到其定义)。此控件添加到表单的代码中,如下所示。根据需要修改
    • trkRotationAngle
      是用于定义图像当前旋转的轨迹栏。将此控件添加到设计器中的窗体中
    • radZoom\u CheckedChanged
      是用于设置当前缩放模式的所有单选按钮的事件处理程序。这些控件设置的值在其
      标记中指定。在设计器中将这些控件添加到窗体中


    @吉米:谢谢你提供的详细信息-对于可视化图形操作中涉及的概念非常有用。我已经找到了一个有效的解决方案(见下面的代码),但是,您的代码使用的步骤效率更高。诚然,我的代码开发更多的是为了学习图像处理的机制——因为我还处于学习曲线的早期阶段。尽管如此,您对力学和技术的说明非常有用

    using System;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;
    
    namespace ZoomImage
    {
        public partial class Form1 : Form
        {
            Image img;
            Bitmap bmp;
            float ang = 0;
            float zoom = 1;
            bool pan;
            bool? ctr = false;
            Point mcurrent;
            PointF mouse;
            PointF image;
            PointF _image;
            PointF rotate;
    
            public Form1()
            {
                InitializeComponent();
                MouseWheel += mouseWheel;
    
                img = Image.FromFile(@"C:\testimage.jpg");
                bmp = new Bitmap(img);
    
                // Set initial scale to fit canvas window
                float wRatio = (float)pbx.Width / (float)img.Width;
                float hRatio = (float)pbx.Height / (float)img.Height;
                zoom = Math.Min(wRatio, hRatio);
                image.X = (pbx.Width - zoom * img.Width) / 2;
                image.Y = (pbx.Height - zoom * img.Height) / 2;
            }
    
            private void label()
            {
                string _imgX = string.Format("{0:000}", image.X);
                string _imgY = string.Format("{0:000}", image.Y);
                lbl1.Text = "Location: " + _imgX + ", " + _imgY + "\r\nRotation: " + ang + "\r\nZoom: " + zoom + "\r\nMouse: " + mcurrent.X + ", " + mcurrent.Y;
            }
    
            private void btnRotate_Click(object sender, EventArgs e)
            {
                if (ModifierKeys == Keys.Control)
                {
                    string msg = "Set center of rotation point:\r\n\nMove mouse to desired center ";
                    msg += "of rotation then hold \"Alt\" and left-click.\r\n\n";
                    msg += "To restore center of rotation to center of image:\r\n\nHold \"Shift\" and";
                    msg += " click \"Rotate\".";
                    MessageBox.Show(msg,"Change center of rotation");
                    ctr = null;
                    pbx.Focus();
                    return;
                } 
                else if (ModifierKeys == Keys.Shift)
                {
                    ctr = false;
                    return;
                }
                ang = ang == 270 ? 0 : ang += 90;
                if (ang > 360) ang -= 360;
                trackBar1.Value = (int)ang;
                ctr = ctr == null ? false : ctr;
                if (ctr == false) rotate = new PointF(img.Width / 2, img.Height / 2);
                pbx.Invalidate();
            }
    
            private void trackBar1_Scroll(object sender, EventArgs e)
            {
                ang = trackBar1.Value;
                if (ctr == false) rotate = new PointF(img.Width / 2, img.Height / 2);
                pbx.Invalidate();
            }
    
            private void mouseWheel(object sender, MouseEventArgs e)
            {
                mouse = new PointF(e.X - image.X, e.Y - image.Y);
    
                float zinc = 0.05f;
                float zfac = 1 + zinc;
                zoom = (float)(e.Delta > 0 ? zoom * (zfac) : zoom / (zfac));
    
                // Adjust "img" (bitmap) orgin to maintain fixed focus @ mouse location
                if (e.Delta > 0)
                {                
                    image.X -= zinc * mouse.X;
                    image.Y -= zinc * mouse.Y;
                }
                else
                {
                    image.X += (1 - 1 / (zfac)) * mouse.X;
                    image.Y += (1 - 1 / (zfac)) * mouse.Y;
                }
                image = new PointF(image.X, image.Y);
                pbx.Invalidate();
            }
    
            private void mouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button != MouseButtons.Left) return;
                if (ModifierKeys == Keys.Alt && ctr == null)
                {
                    ctr = true;
                    rotate = new PointF((e.X - image.X) / zoom, (e.Y - image.Y) / zoom);
                    return;
                }
                pan = true;
                mouse = e.Location;
                _image = image;
            }
    
            private void mouseMove(object sender, MouseEventArgs e)
            {
                mcurrent = e.Location;
                label();
    
                if (e.Button != MouseButtons.Left || !pan) return;
                image.X = _image.X + e.X - mouse.X;
                image.Y = _image.Y + e.Y - mouse.Y;
                image = new PointF(image.X, image.Y);
                pbx.Invalidate();
            }
    
            private void mouseUp(object sender, MouseEventArgs e)
            {
                pan = false;
            }
    
            private void pbx_Paint(object sender, PaintEventArgs e)
            {
                label();
    
                // Generate bitmap "bmp"  - this can be saved as drawn...if deisred
                bmp = new Bitmap(img.Width, img.Height);
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    Matrix transform = new Matrix();
                    transform.Scale(zoom, zoom, MatrixOrder.Append);
                    transform.RotateAt(ang, rotate);
                    transform.Translate(image.X, image.Y, MatrixOrder.Append);
                    g.Transform = transform;
                    g.DrawImage(img, 0, 0);
                }
                e.Graphics.DrawImage(bmp, 0, 0);
            }
        }
    }
    

    …仍在寻找关于这个问题的一些想法。。。
    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;
        }
    }
    
    using System;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;
    
    namespace ZoomImage
    {
        public partial class Form1 : Form
        {
            Image img;
            Bitmap bmp;
            float ang = 0;
            float zoom = 1;
            bool pan;
            bool? ctr = false;
            Point mcurrent;
            PointF mouse;
            PointF image;
            PointF _image;
            PointF rotate;
    
            public Form1()
            {
                InitializeComponent();
                MouseWheel += mouseWheel;
    
                img = Image.FromFile(@"C:\testimage.jpg");
                bmp = new Bitmap(img);
    
                // Set initial scale to fit canvas window
                float wRatio = (float)pbx.Width / (float)img.Width;
                float hRatio = (float)pbx.Height / (float)img.Height;
                zoom = Math.Min(wRatio, hRatio);
                image.X = (pbx.Width - zoom * img.Width) / 2;
                image.Y = (pbx.Height - zoom * img.Height) / 2;
            }
    
            private void label()
            {
                string _imgX = string.Format("{0:000}", image.X);
                string _imgY = string.Format("{0:000}", image.Y);
                lbl1.Text = "Location: " + _imgX + ", " + _imgY + "\r\nRotation: " + ang + "\r\nZoom: " + zoom + "\r\nMouse: " + mcurrent.X + ", " + mcurrent.Y;
            }
    
            private void btnRotate_Click(object sender, EventArgs e)
            {
                if (ModifierKeys == Keys.Control)
                {
                    string msg = "Set center of rotation point:\r\n\nMove mouse to desired center ";
                    msg += "of rotation then hold \"Alt\" and left-click.\r\n\n";
                    msg += "To restore center of rotation to center of image:\r\n\nHold \"Shift\" and";
                    msg += " click \"Rotate\".";
                    MessageBox.Show(msg,"Change center of rotation");
                    ctr = null;
                    pbx.Focus();
                    return;
                } 
                else if (ModifierKeys == Keys.Shift)
                {
                    ctr = false;
                    return;
                }
                ang = ang == 270 ? 0 : ang += 90;
                if (ang > 360) ang -= 360;
                trackBar1.Value = (int)ang;
                ctr = ctr == null ? false : ctr;
                if (ctr == false) rotate = new PointF(img.Width / 2, img.Height / 2);
                pbx.Invalidate();
            }
    
            private void trackBar1_Scroll(object sender, EventArgs e)
            {
                ang = trackBar1.Value;
                if (ctr == false) rotate = new PointF(img.Width / 2, img.Height / 2);
                pbx.Invalidate();
            }
    
            private void mouseWheel(object sender, MouseEventArgs e)
            {
                mouse = new PointF(e.X - image.X, e.Y - image.Y);
    
                float zinc = 0.05f;
                float zfac = 1 + zinc;
                zoom = (float)(e.Delta > 0 ? zoom * (zfac) : zoom / (zfac));
    
                // Adjust "img" (bitmap) orgin to maintain fixed focus @ mouse location
                if (e.Delta > 0)
                {                
                    image.X -= zinc * mouse.X;
                    image.Y -= zinc * mouse.Y;
                }
                else
                {
                    image.X += (1 - 1 / (zfac)) * mouse.X;
                    image.Y += (1 - 1 / (zfac)) * mouse.Y;
                }
                image = new PointF(image.X, image.Y);
                pbx.Invalidate();
            }
    
            private void mouseDown(object sender, MouseEventArgs e)
            {
                if (e.Button != MouseButtons.Left) return;
                if (ModifierKeys == Keys.Alt && ctr == null)
                {
                    ctr = true;
                    rotate = new PointF((e.X - image.X) / zoom, (e.Y - image.Y) / zoom);
                    return;
                }
                pan = true;
                mouse = e.Location;
                _image = image;
            }
    
            private void mouseMove(object sender, MouseEventArgs e)
            {
                mcurrent = e.Location;
                label();
    
                if (e.Button != MouseButtons.Left || !pan) return;
                image.X = _image.X + e.X - mouse.X;
                image.Y = _image.Y + e.Y - mouse.Y;
                image = new PointF(image.X, image.Y);
                pbx.Invalidate();
            }
    
            private void mouseUp(object sender, MouseEventArgs e)
            {
                pan = false;
            }
    
            private void pbx_Paint(object sender, PaintEventArgs e)
            {
                label();
    
                // Generate bitmap "bmp"  - this can be saved as drawn...if deisred
                bmp = new Bitmap(img.Width, img.Height);
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    Matrix transform = new Matrix();
                    transform.Scale(zoom, zoom, MatrixOrder.Append);
                    transform.RotateAt(ang, rotate);
                    transform.Translate(image.X, image.Y, MatrixOrder.Append);
                    g.Transform = transform;
                    g.DrawImage(img, 0, 0);
                }
                e.Graphics.DrawImage(bmp, 0, 0);
            }
        }
    }