(The images show the "home computers" in early days)
Introduction
In this article, I'll give a demo of how to crop an image by selecting a region with the mouse.
Background
We select a region of an image, create another image of the selected region, and zoom the new image to the size of the PictureBox
.
Although the PictureBox
has its the SizeMode = PictureBoxSizeMode.Zoom
, a MemoryOutOfRange
exception may be thrown when cropping the image several times. So, I assume this property is just for fitting an image to a PictureBox
once when loading the form.
Extension methods for Image
I've implemented the cropping and fitting to the PictureBox
as an extension to the Image
class.
Cropping
The image is cropped by cloning a region of the original image.
public static Image Crop(this Image image, Rectangle selection)
{
Bitmap bmp = image as Bitmap;
if (bmp == null)
throw new ArgumentException("No valid bitmap");
Bitmap cropBmp = bmp.Clone(selection, bmp.PixelFormat);
image.Dispose();
return cropBmp;
}
Fitting the image to the PictureBox
As mentioned under the Background section, fitting can't be done by the PictureBox
itself. So, I've coded this myself.
As the first step, the scale factors in the vertical and horizontal directions are calculated. To scale (or zoom) the image so that the original ratio of width to height is maintained, the bigger ratio of the scale is used. The interpolation-mode is set to produce high quality pictures.
public static Image Fit2PictureBox(this Image image, PictureBox picBox)
{
Bitmap bmp = null;
Graphics g;
double scaleY = (double)image.Width / picBox.Width;
double scaleX = (double)image.Height / picBox.Height;
double scale = scaleY < scaleX ? scaleX : scaleY;
bmp = new Bitmap(
(int)((double)image.Width / scale),
(int)((double)image.Height / scale));
bmp.SetResolution(
image.HorizontalResolution,
image.VerticalResolution);
g = Graphics.FromImage(bmp);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(
image,
new Rectangle(
0, 0,
bmp.Width, bmp.Height),
new Rectangle(
0, 0,
image.Width, image.Height),
GraphicsUnit.Pixel);
g.Dispose();
image.Dispose();
return bmp;
}
The user interface (for the demo)
Restore original image
To restore the original image, it is saved in the form-load event. Notice, save a copy of the image and not a reference. In the latter case, the copy would point to the (cropped) image in the PictureBox
. Also, on restoring, a copy is assigned to the PictureBox
.
private Image _originalImage;
private void Form1_Load(object sender, System.EventArgs e)
{
_originalImage = pictureBox1.Image.Clone() as Image;
}
private void button1_Click(object sender, System.EventArgs e)
{
pictureBox1.Image = _originalImage.Clone() as Image;
}
Selecting the region
Selecting a region is straightforward. Just use the MouseDown
and MouseMove
events.
private bool _selecting;
private Rectangle _selection;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_selecting = true;
_selection = new Rectangle(new Point(e.X, e.Y), new Size());
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (_selecting)
{
_selection.Width = e.X - _selection.X;
_selection.Height = e.Y - _selection.Y;
pictureBox1.Refresh();
}
}
To display the selection, a rectangle is drawn on the picture.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_selecting)
{
Pen pen = Pens.GreenYellow;
e.Graphics.DrawRectangle(pen, _selection);
}
}
Cropping
The end of selection is indicated by a MouseUp
. Before cropping, the size of the selection is validated because it could be zero in the case of double-clicking in the PictureBox
.
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left &&
_selecting &&
_selection.Size != new Size())
{
Image img = pictureBox1.Image.Crop(_selection);
pictureBox1.Image = img.Fit2PictureBox(pictureBox1);
_selecting = false;
}
else
_selecting = false;
}
History
- 5th November 2008: Initial release.