简体   繁体   English

如何放大面板内PictureBox的绘制矩形?

[英]How zoom into a drawn rectangle of picturebox which is inside a panel?

I have a PictureBox inside a Panel in order to zoom and pan. 在面板内有一个PictureBox ,以便缩放和平移。 I created the possibility to select 4 points with the mouse click and draw a rectangle on the PictureBox. 我创建了通过单击鼠标来选择4点并在PictureBox上绘制矩形的可能性。 Once the rectangle is over my picture I pass the coordinates of the rectangle to the method "cropRectangle". 一旦矩形在我的图片上,我就将矩形的坐标传递给方法“ cropRectangle”。 This method crops the rectangle and replace the old image with the cropped one. 此方法裁剪矩形并用裁剪后的图像替换旧图像。 This works very well : 这很好用

( OriginalImage is the bitmap of the actual image in the pictureBox ) OriginalImage是pictureBox中实际图像的位图

private void cropRectangle(Rectangle rect){
    double left = (rect.X) * originalImage.Width / pictureBox.Width,
    top = (rect.Y) * originalImage.Width / pictureBox.Height,
    right = (rect.Width) * originalImage.Width / pictureBox.Width,
    bottom = (rect.Height) * originalImage.Height / pictureBox.Height;
    rect = new Rectangle (Convert.ToInt32(left), Convert.ToInt32(top), Convert.ToInt32(right), Convert.ToInt32(bottom));

    Bitmap bitmap = orignalImage.Clone(rect, originalImage.PixelFormat);
    pictureBox.Image = (Image)bitmap; 

    centerPictureBox();

    // fit image into pictureBox with respect to the ratio
    float ratio = orignalImage.Width / orignalImage.Height;
    pictureBox.Width = panel.Width;
    pictureBox.Height = Convert.ToInt32(pictureBox.Width * ratio);
    centerPictureBox();
}

What I am trying to do now is to zoom the selected area instead to crop it. 我现在想要做的是缩放所选区域而不是对其进行裁剪。 The rectangle of the picturebox has to match with the panel. 图片框的矩形必须与面板匹配。

How can I show only the selected area (rectangle) of the picturebox through the panel without cropping the image? 如何通过面板仅显示图片框的选定区域(矩形),而不裁剪图像?

You should stick with modifying the existing Bitmap using the Graphics object instead of changing the size of the PictureBox. 您应该坚持使用Graphics对象修改现有的Bitmap ,而不要更改PictureBox的大小。 You don't want to be tied to a UI control when the desired functionality is already available elsewhere. 当所需的功能在其他地方已经可用时,您不希望被绑定到UI控件。

Here are rough steps to achieve that: 以下是实现该目标的粗略步骤:

  1. Create a temporary Bitmap object that will store the zoomed image. 创建一个临时的Bitmap对象,该对象将存储缩放的图像。 Bitmap tBitmap = new Bitmap(zoomX, zoomY, PixelFormat.Format24bppRgb);
  2. Calculate the zoom factors and stuff like you already do (I didn't check if the code is correct but I assume that it is) when you want to zoom. 当您要缩放时,计算缩放系数和您已经做过的事情(我没有检查代码是否正确,但我认为是正确的)。
  3. Create a new Graphics object from the temporary bitmap. 从临时位图创建一个新的Graphics对象。 Graphics graphics = Graphics.FromImage(tBitmap);
  4. Set the InterpolationMode so that the image is scaled with a good quality. 设置InterpolationMode,以便以良好的质量缩放图像。 graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  5. Draw the zoomed image using the DrawImage method (using the original image from the PictureBox). 使用DrawImage方法绘制缩放的图像(使用PictureBox中的原始图像)。 graphics.DrawImage(pictureBox.Image, new Rectangle(0, 0, pictureBox.Width, pictureBox.Height), new Rectangle(/*The crop rectangle you draw already*/), GraphicsUnit.Pixel);
  6. Set the newly drawn bitmap as the Image in your PictureBox. 将新绘制的位图设置为PictureBox中的Image。 pictureBox.Image = tBitmap;
  7. Remember to dispose of the graphics object we used for drawing. 记住要处理掉我们用于绘图的图形对象。 graphics.Dispose();
  8. You might need to refresh the PictureBox to force it to redraw itself. 您可能需要刷新PictureBox才能强制其重新绘制。 pictureBox.Refresh();

Those are the basic steps to follow. 这些是要遵循的基本步骤。 I didn't have time to go through your existing code that deeply so you might need to change some additional things to make it work. 我没有时间深入研究现有代码,因此您可能需要更改一些其他内容才能使其正常工作。

Here's also an MSDN article that covers the same stuff: Cropping and Scaling Images in GDI+ 这也是一篇MSDN文章,涵盖了相同的内容: 在GDI +中裁剪和缩放图像

You might be interested in this control (ZoomPicBox) which allows for zooming and panning a picture box. 您可能对此控件(ZoomPicBox)感兴趣,该控件可用于缩放和平移图片框。

All credit for this code is Bob Powell and it was taken from his site (which seems to be down now and has been for a long time now.). 该代码的全部功劳是鲍勃·鲍威尔(Bob Powell),它是从他的网站上获取的(现在看来该网站已经关闭,并且已经存在了很长时间)。

I copied the code from archive.org at this link: 我在以下链接中从archive.org复制了代码:

https://web.archive.org/web/20080313161349/http://www.bobpowell.net/zoompicbox.htm https://web.archive.org/web/20080313161349/http://www.bobpowell.net/zoompicbox.htm

That link has additional information and is worth a read. 该链接具有其他信息,值得一读。 The code is available in VB.Net as well. 该代码在VB.Net中也可用。

I don't know why Bob Powell's site is down, but it was a great site for Windows Graphics information. 我不知道鲍勃·鲍威尔(Bob Powell)的网站为什么倒塌,但是它是Windows Graphics信息的绝佳站点。

I felt this code was worth repeating. 我觉得这段代码值得重复。 This control can be dragged onto the form. 可以将该控件拖到窗体上。

namespace bobpowell.net
{
  /// <summary>
  /// ZoomPicBox does what it says on the wrapper.
  /// </summary>
  /// <remarks>
  /// PictureBox doesn't lend itself well to overriding. Why not start with something basic and do the job properly?
  /// </remarks>
  public class ZoomPicBox : ScrollableControl
  {

    Image _image;
    [
    Category("Appearance"),
    Description("The image to be displayed")
    ]
    public Image Image
    {
      get{return _image;}
      set
      {
        _image=value;
        UpdateScaleFactor();
        Invalidate();
      }
    }

    float _zoom=1.0f;
    [
    Category("Appearance"),
    Description("The zoom factor. Less than 1 to reduce. More than 1 to magnify.")
    ]
    public float Zoom
    {
      get{return _zoom;}
      set
      {
        if(value<0 || value<0.00001)
          value=0.00001f;
        _zoom=value;
        UpdateScaleFactor();
        Invalidate();
      }
    }

    /// <summary>
    /// Calculates the effective size of the image
    ///after zooming and updates the AutoScrollSize accordingly
    /// </summary>
    private void UpdateScaleFactor()
    {
      if(_image==null)
        this.AutoScrollMinSize=this.Size;
      else
      {
        this.AutoScrollMinSize=new Size(
          (int)(this._image.Width*_zoom+0.5f),
          (int)(this._image.Height*_zoom+0.5f)
          );
      }
    }

    InterpolationMode _interpolationMode=InterpolationMode.High;
    [
    Category("Appearance"),
    Description("The interpolation mode used to smooth the drawing")
    ]
    public InterpolationMode InterpolationMode
    {
      get{return _interpolationMode;}
      set{_interpolationMode=value;}
    }


    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
      // do nothing.
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      //if no image, don't bother
      if(_image==null)
      {
        base.OnPaintBackground(e);
        return;
      }
      //Set up a zoom matrix
      Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);
      //now translate the matrix into position for the scrollbars
      mx.Translate(this.AutoScrollPosition.X / _zoom, this.AutoScrollPosition.Y / _zoom);
      //use the transform
      e.Graphics.Transform=mx;
      //and the desired interpolation mode
      e.Graphics.InterpolationMode=_interpolationMode;
      //Draw the image ignoring the images resolution settings.
      e.Graphics.DrawImage(_image,new Rectangle(0,0,this._image.Width,this._image.Height),0,0,_image.Width, _image.Height,GraphicsUnit.Pixel);
      base.OnPaint (e);
    }


    public ZoomPicBox()
    {
      //Double buffer the control
      this.SetStyle(ControlStyles.AllPaintingInWmPaint |
        ControlStyles.UserPaint |
        ControlStyles.ResizeRedraw |
        ControlStyles.UserPaint |
        ControlStyles.DoubleBuffer, true);

      this.AutoScroll=true;
    }
  }
}

I have found a elegant solution to my problem: 我找到了解决问题的好方法:

private void zoomInsideRectangle(Rectangle rect){
    float zoomFactor = ((float)panel.Width / rect.Width) - 1;

    pictureBox.Width = pictureBox.Width + convertToIntPerfect(pictureBox.Width * zoomFactor);
    pictureBox.Height = pictureBox.Height + convertToIntPerfect(pictureBox.Height * zoomFactor);

    rect.X = rect.X + convertToIntPerfect(rect.X * zoomFactor);
    rect.Y = rect.Y + convertToIntPerfect(rect.Y * zoomFactor);

    pictureBox.Left = convertToIntPerfect(-rect.X);
    pictureBox.Top = convertToIntPerfect(-rect.Y);
}

Since I know the length of the panel where I can see the picturebox. 由于我知道可以看到图片框的面板的长度。 I take the ratio of the panel and the width of my rectangle that I want to zoom in. This ratio is my zoomratio. 我将要放大的面板比例和矩形的宽度。此比例就是我的缩放比例。

I multiply the size of the picturebox with the ratio that I calculated. 我将图片框的大小乘以我计算出的比率。

I anchor the picturebox by the left and top with the coordinates of my rectangle. 我将图片框固定在矩形的左侧和顶部。 But right before doing that I have to multiply my coordinates of my rectangle with the zoomratio since I changed the size of the picturebox. 但是在此之前,由于更改了图片框的大小,因此必须将矩形的坐标乘以缩放比例。

I didn't implemented the Y transformation since the original ratio of the image will be damaged. 由于图像的原始比例会被损坏,因此我没有实施Y转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM