简体   繁体   中英

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. I created the possibility to select 4 points with the mouse click and draw a rectangle on the PictureBox. Once the rectangle is over my picture I pass the coordinates of the rectangle to the method "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 )

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. You don't want to be tied to a UI control when the desired functionality is already available elsewhere.

Here are rough steps to achieve that:

  1. Create a temporary Bitmap object that will store the zoomed image. 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.FromImage(tBitmap);
  4. Set the InterpolationMode so that the image is scaled with a good quality. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  5. Draw the zoomed image using the DrawImage method (using the original image from the 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 = 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.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+

You might be interested in this control (ZoomPicBox) which allows for zooming and panning a picture box.

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.).

I copied the code from archive.org at this link:

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.

I don't know why Bob Powell's site is down, but it was a great site for Windows Graphics information.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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