PictureBox处于“缩放”模式时裁剪图像的正确部分[重复]

Posted

技术标签:

【中文标题】PictureBox处于“缩放”模式时裁剪图像的正确部分[重复]【英文标题】:Crop correct part of image while the PictureBox is in 'zoom' mode [duplicate] 【发布时间】:2019-10-15 00:49:51 【问题描述】:

我有一个 PictureBox1,它的 sizemode 设置为 Stretch 和 PictureBox1。 PictureBox1 包含一个图像,让我选择它的一部分,然后裁剪它并将裁剪的部分存储在 PictureBox2 中。 当 sizemode 设置为 Stretch 且图片未缩放时效果很好,但当我缩放它或将 sizemode 设置为缩放时则不行。

working example - sizemode set to 'stretch'

我用来裁剪部分图片的代码 (original source)

try

    float stretch1X = 1f * pictureBox1.Image.Width / pictureBox1.ClientSize.Width;
    float stretch1Y = 1f * pictureBox1.Image.Height / pictureBox1.ClientSize.Height;

    Point pt = new Point((int)(_mDown.X * stretch1X), (int)(_mDown.Y * stretch1Y));
    Size sz = new Size((int)((_mCurr.X - _mDown.X) * stretch1X),
                       (int)((_mCurr.Y - _mDown.Y) * stretch1Y));



    if (sz.Width > 0 && sz.Height > 0)
    
        Rectangle rSrc = new Rectangle(pt, sz);
        Rectangle rDest = new Rectangle(Point.Empty, sz);

        Bitmap bmp = new Bitmap(sz.Width, sz.Height);
        using (Graphics G = Graphics.FromImage(bmp))
            G.DrawImage(pictureBox1.Image, rDest, rSrc, GraphicsUnit.Pixel);
        return bmp;
    

    return null;

catch (Exception ex)

    throw ex;

如何正确计算?如何使裁剪功能以某种方式工作,以便用户放大/缩小并仍然裁剪图片的正确部分?

【问题讨论】:

当它被缩放然后stretch1X == stretch1Y。 Look here. 如何将它应用到我的场景中?你能再解释一下吗?你的解释对我来说不是很清楚@HansPassant 您需要知道显示图像的大小,该方法会计算它。只需复制缩放案例的代码。一旦你知道了这个大小,你可以将它除以 image.Width 来知道stretch1X。因此stretch1Y。显示的图像将被水平或垂直的信箱,由显示的图像大小和 PictureBox.ClientSize 之间的差异除以 2 偏移。 这个有点混乱,能不能根据我上面贴的代码贴一些提示代码?提前感谢@HansPassant @Hans:你说的很对!有趣的是我怎么找不到那个帖子..!?我留下答案是因为它还会创建裁剪后的图像并证明它也可以在缩放模式下工作.. 【参考方案1】:

您需要使用拉伸因子以及偏移量来计算点。

对于Zoom,只有一个因素,因为ImagePictureBox的纵横比始终相同,但通常有一个偏移量;对于Stretch,您不需要偏移,但需要两个因素。

这是一个使用 two PictureBoxes 的示例,其中两个显示缩放版本和裁剪的位图。它利用了一个通用函数ImageArea 来确定大小和偏移量。

两个类级变量:

Point pDown = Point.Empty;
Rectangle rect = Rectangle.Empty;

三个鼠标事件:

private void PictureBox1_MouseDown(object sender, MouseEventArgs e)

    pDown = e.Location;
    pictureBox1.Refresh();


private void PictureBox1_MouseMove(object sender, MouseEventArgs e)

    if (!e.Button.HasFlag(MouseButtons.Left)) return;

    rect = new Rectangle(pDown, new Size(e.X - pDown.X, e.Y - pDown.Y));
    using (Graphics g = pictureBox1.CreateGraphics())
    
        pictureBox1.Refresh();
        g.DrawRectangle(Pens.Orange, rect);
    


private void PictureBox1_MouseUp(object sender, MouseEventArgs e)

    Rectangle iR = ImageArea(pictureBox2);
    rect = new Rectangle(pDown.X - iR.X, pDown.Y - iR.Y, 
                         e.X - pDown.X, e.Y - pDown.Y);
    Rectangle rectSrc = Scaled(rect, pictureBox2, true);
    Rectangle rectDest = new Rectangle(Point.Empty, rectSrc.Size);

    Bitmap bmp = new Bitmap(rectDest.Width, rectDest.Height);
    using (Graphics g = Graphics.FromImage(bmp))
    
        g.DrawImage(pictureBox2.Image, rectDest, rectSrc, GraphicsUnit.Pixel);
    
    pictureBox2.Image = bmp;

这是一个有用的函数,它返回图片框内任何尺寸模式的实际图像的区域..:

Rectangle ImageArea(PictureBox pbox)

    Size si = pbox.Image.Size;
    Size sp = pbox.ClientSize;

    if (pbox.SizeMode == PictureBoxSizeMode.StretchImage) 
       return pbox.ClientRectangle;
    if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
        pbox.SizeMode == PictureBoxSizeMode.AutoSize) 
       return new Rectangle(Point.Empty, si);
    if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
        return new Rectangle(new Point((sp.Width - si.Width) / 2,
                            (sp.Height - si.Height) / 2), si);

    //  PictureBoxSizeMode.Zoom
    float ri = 1f * si.Width / si.Height;
    float rp = 1f * sp.Width / sp.Height;
    if (rp > ri)
    
        int width = si.Width * sp.Height / si.Height;
        int left = (sp.Width - width) / 2;
        return new Rectangle(left, 0, width, sp.Height);
    
    else
    
        int height = si.Height * sp.Width / si.Width;
        int top = (sp.Height - height) / 2;
        return new Rectangle(0, top, sp.Width, height);
    

我们只需要偏移来确定未缩放的矩形。我们还需要对其进行扩展:

Rectangle Scaled(Rectangle rect, PictureBox pbox, bool scale)

    float factor = GetFactor(pbox);
    if (!scale) factor = 1f / factor;
    return Rectangle.Round(new RectangleF(rect.X * factor, rect.Y * factor,  
                               rect.Width * factor, rect.Height * factor));

为此需要知道缩放因子,它取决于纵横比:

float GetFactor(PictureBox pBox)

    if (pBox.Image == null) return 0;
    Size si = pBox.Image.Size;
    Size sp = pBox.ClientSize;
    float ri = 1f * si.Width / si.Height;
    float rp = 1f * sp.Width / sp.Height;
    float factor = 1f * pBox.Image.Width / pBox.ClientSize.Width;
    if (rp > ri) factor = 1f * pBox.Image.Height / pBox.ClientSize.Height;
    return factor;

如果 PictureBox 通过将 AutoScrolling Panel 放置在 AutoScrolling Panel 内并更改 Pbox.Size放大或缩小,此解决方案也将起作用。

【讨论】:

以上是关于PictureBox处于“缩放”模式时裁剪图像的正确部分[重复]的主要内容,如果未能解决你的问题,请参考以下文章

当图像重新缩放以适合画布时,织物 js 上的裁剪功能无法正常工作

SwiftUI - 缩放、缩放和裁剪图像

如何检索 WinForms PictureBox 的缩放因子?

如何缩放 + 裁剪图像并在 imageview 上显示裁剪的图像

图像的裁剪

如何在网站中动态裁剪/缩放图像