Reputation: 161
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?
Upvotes: 0
Views: 3751
Reputation: 11216
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;
}
}
}
Upvotes: 1
Reputation: 161
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.
Upvotes: 0
Reputation: 7189
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:
Bitmap
object that will store the zoomed image. Bitmap tBitmap = new Bitmap(zoomX, zoomY, PixelFormat.Format24bppRgb);
Graphics graphics = Graphics.FromImage(tBitmap);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(pictureBox.Image, new Rectangle(0, 0, pictureBox.Width, pictureBox.Height), new Rectangle(/*The crop rectangle you draw already*/), GraphicsUnit.Pixel);
pictureBox.Image = tBitmap;
graphics.Dispose();
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+
Upvotes: 1