如何在 C# windows 窗体中绘制可缩放图像

Posted

技术标签:

【中文标题】如何在 C# windows 窗体中绘制可缩放图像【英文标题】:How to Draw on Zoomable Image in C# windows Forms 【发布时间】:2015-04-22 09:29:42 【问题描述】:

所以我正在实现一个可以读取图像平移、缩放和做其他事情的项目。一切都很顺利,直到我尝试用鼠标右键实现绘图。

问题是当我画一条线时,出现在图像上的线与我在屏幕上画的线不对应,这意味着它发生了偏移,我知道它是因为图像的重新调整大小和缩放,但是当我在图像上用其原始大小(图像)和平移画线时;我没问题。

这是代码。

首先这里是当我点击浏览并选择图片时如何加载图片

Myimage = new Bitmap(ImagePath);
resized = myImage.Size;
imageResize();
pictureBox.Paint += new    System.Windows.Forms.PaintEventHandler(this.pictureBox_Paint);
                pictureBox.Invalidate();

imageResize 函数执行以下操作:

void imageResize()
     
//calculated the size to fit the control i will draw the image on   
 resized.Height = someMath;
 resized.Width = someMath;

然后在我写的 pictureBox_Paint 事件的事件处理程序中:

private void pictureBox_Paint(object sender,      System.Windows.Forms.PaintEventArgs e)

// Create a local version of the graphics object for the PictureBox.
Graphics PboxGraphics = e.Graphics;
PboxGraphics.DrawImage(myImage, imageULcorner.X, imageULcorner.Y,     resized.Width, resized.Height);

如您所见,调整后的大小不是原始图像大小,我这样做是因为我希望图像显示在图片框控件上集中并填充现在下一部分是我的问题开始的地方

我必须使用鼠标右键在图像上画线,所以我实现了 pictureBox_MouseDown 和 pictureBox_MouseUp 事件处理程序

// mouse down event handler
private void pictureBox_MouseDown(object sender, MouseEventArgs e)

else if (mouse.Button == MouseButtons.Right)

mouseDown = mouse.Location;
mouseDown.X = mouseDown.X - imageULcorner.X;
mouseDown.Y = mouseDown.Y - imageULcorner.Y;
draw = true;


这里是鼠标向上事件处理程序

//Mouse UP
private void pictureBox_MouseUp(object sender, MouseEventArgs e)

else if (mouse.Button == MouseButtons.Right)

if (draw)
 
mouseLocationNow.X = mouse.X - imageULcorner.X;
mouseLocationNow.Y = mouse.Y - imageULcorner.Y;
//
// get graphics object of the image ( the original not the resized)
// as the resized image only appears when i draw on the graphics of the
// pictureBox control
// i know the problem lies here but how can i fix it
//
Graphics image = Graphics.FromImage(myImage);
Pen pen = new Pen(Color.Red, 2);
image.DrawLine(pen, mouseLocationNow, mouseDown);
pictureBox.Invalidate();

draw = false;

所以最后我希望能够在调整大小的图像上绘制并使其与真实图像以及我画线的屏幕相对应 感谢和抱歉发了这么长的帖子,但这个问题让我发疯了。

【问题讨论】:

简短版本是:您需要 a) 计算鼠标事件中的点以适应缩放(向后,因为您瞄准的是缩放的世界)和 b) 缩放图形对象(向前,使用矩阵变换)到与图片框中的图像相同的缩放比例。 如果您愿意使用 WPF,我过去曾问过类似的问题。问题和答案可能会有所帮助 - ***.com/questions/14729853/… @TaW 我知道 (A) 但我不知道 (B) 存在...听起来像是找到解决方案的好方法...谢谢。 使用 Matrix 来完成缩放(这是一个好主意),然后您也可以获得逆矩阵并使用它将用户输入的鼠标坐标转换回您的坐标空间图片。基本上,您最终在原始图像坐标空间中完成所有实际工作,使用矩阵将用户输入转换回图像坐标空间,并从图像坐标空间(即图像本身和任何其他渲染在Paint 事件期间将其顶部(例如选择矩形)返回到屏幕。 虽然我猜你可以独自处理彼得和我给你的提示,但我认为添加一个代码示例以供将来参考也不会受到伤害.. 【参考方案1】:

这是一个PictureBox 子类,它不仅支持将缩放应用到Image,还支持你在其表面上绘制的图形。

它包含一个SetZoom 函数,可通过缩放自身和矩阵来放大。

它还有一个ScalePoint 函数,可用于根据鼠标事件中收到的像素坐标计算未缩放的坐标。

这个想法是使用Transformation Matrix 来缩放Graphics 对象将在Paint 事件中绘制的任何像素。

我在表单中包含了一些用于测试的代码。

public partial class ScaledPictureBox : PictureBox

    public Matrix ScaleM  get; set; 

    float Zoom  get; set; 
    Size ImgSize  get; set; 

    public ScaledPictureBox()
    
        InitializeComponent();
        ScaleM = new Matrix();
        SizeMode = PictureBoxSizeMode.Zoom;
    

    public void InitImage()
    
        if (Image != null)
        
            ImgSize = Image.Size;
            Size = ImgSize;
            SetZoom(100);
        
    

    public void SetZoom(float zoomfactor)
    
        if (zoomfactor <= 0) throw new Exception("Zoom must be positive");
        float oldZoom = Zoom;
        Zoom = zoomfactor / 100f;
        ScaleM.Reset();
        ScaleM.Scale(Zoom , Zoom );
        if (ImgSize != Size.Empty) Size = new Size((int)(ImgSize.Width * Zoom), 
                                                   (int)(ImgSize.Height * Zoom));

    

    public PointF ScalePoint(PointF pt)
       return new PointF(pt.X / Zoom , pt.Y / Zoom );     


这是表单中进行测试的代码:

public List<PointF> somePoints = new List<PointF>();

private void scaledPictureBox1_MouseClick(object sender, MouseEventArgs e)

    somePoints.Add(scaledPictureBox1.ScalePoint(e.Location) );
    scaledPictureBox1.Invalidate();


private void scaledPictureBox1_Paint(object sender, PaintEventArgs e)

    // here we apply the scaling matrix to the graphics object:
    e.Graphics.MultiplyTransform(scaledPictureBox1.ScaleM);
    using (Pen pen = new Pen(Color.Red, 10f))
    
        PointF center = new PointF(scaledPictureBox1.Width / 2f, 
                                   scaledPictureBox1.Height / 2f);
        center = scaledPictureBox1.ScalePoint(center);
        foreach (PointF pt in somePoints)
        
            DrawPoint(e.Graphics, pt, pen);
            e.Graphics.DrawLine(Pens.Yellow, center, pt);
        
    


public void DrawPoint(Graphics G, PointF pt, Pen pen)

    using (SolidBrush brush = new SolidBrush(pen.Color))
    
        float pw = pen.Width;
        float pr = pw / 2f;
        G.FillEllipse(brush, new RectangleF(pt.X - pr, pt.Y - pr, pw, pw));
    

以下是在四种不同缩放设置中绘制几个相同点后的结果; ScaledPictureBox 显然放在AutoScroll-Panel 中。这些线条显示了如何使用常规绘图命令..

【讨论】:

非常感谢您的提示已经足够了:) 但是感谢您为编写代码所做的所有努力,当然它看起来更有效率和更有条理.. 不幸的是,给我这个任务的人明确要求不使用 C# 中的转换来实现它。我已经发现了我的代码的问题,但我不会发布我的答案,因为我认为你的更好。 有趣。如果没有变换缩放,坐标也将起作用。在那里你还需要缩放笔的宽度,然后是一些.. 一个很好的答案,为我解决了一个长期存在的问题(让 winforms 设计师编辑布局)。谢谢!

以上是关于如何在 C# windows 窗体中绘制可缩放图像的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 UIScrollView 和 CATiledLayer 在可缩放的 UIView 上绘制标记

如何从Python列表中绘制可缩放的Cantor集?

在可缩放的滚动视图中禁用图像视图缩放

jQuerycropit 插件检查当前图像是不是可缩放

C# SkiaSharp OpenTK Winform - 如何从后台线程中绘制?

如何通过在 C# windows 窗体中的文本框中输入来更改图像的大小?