Title: Rotate a picture in C#
I recently returned from a trip and one particular picture was rotated just a tiny bit from where I wanted it. Normally I wouldn't care but this picture (a small version is included in the download) has horizontal features at the bottom that made it look off. So I wrote this program to let me rotate the picture by small amounts until it was the way I wanted it.
This program performs two main tasks. First it rotates a picture. Second it displays the result at various scales.
Rotating the Picture
When the program loads a new image or when you change the value in the Angle text box, the following code rotates and displays the image.
// Display the image at the current rotation and scale.
private void DisplayImage()
{
if (OriginalImage == null) return;
RotatedImage = null;
picRotatedImage.Visible = false;
float angle;
if (!float.TryParse(txtAngle.Text, out angle)) return;
// Find the size of the image's rotated bounding box.
Matrix rotation = new Matrix();
rotation.Rotate(angle);
int old_wid = OriginalImage.Width;
int old_hgt = OriginalImage.Height;
PointF[] points =
{
new PointF(0, 0),
new PointF(old_wid, 0),
new PointF(0, old_hgt),
new PointF(old_wid, old_hgt),
};
rotation.TransformPoints(points);
float[] xs =
{ points[0].X, points[1].X, points[2].X, points[3].X };
float[] ys =
{ points[0].Y, points[1].Y, points[2].Y, points[3].Y };
int new_wid = (int)(xs.Max() - xs.Min());
int new_hgt = (int)(ys.Max() - ys.Min());
// Make the rotated image.
RotatedImage = new Bitmap(new_wid, new_hgt);
using (Graphics gr = Graphics.FromImage(RotatedImage))
{
gr.TranslateTransform(-old_wid / 2, -old_hgt / 2,
MatrixOrder.Append);
gr.RotateTransform(angle, MatrixOrder.Append);
gr.TranslateTransform(new_wid / 2, new_hgt / 2,
MatrixOrder.Append);
RectangleF source_rect = new RectangleF(0, 0,
OriginalImage.Width, OriginalImage.Height);
PointF[] dest_points =
{
new PointF(0, 0),
new PointF(OriginalImage.Width, 0),
new PointF(0, OriginalImage.Height),
};
gr.DrawImage(OriginalImage, dest_points, source_rect,
GraphicsUnit.Pixel);
}
// Scale the output PictureBox.
SetPictureBoxSize();
// Display the result.
picRotatedImage.Image = RotatedImage;
picRotatedImage.Visible = true;
}
The code first checks whether an image is currently loaded and, if it is not, the method simply returns.
The code clears any existing rotated image and then gets the angle you entered in the text box. If the value in the text box isn't a floating point number, the method also returns.
Next the code finds the size that the rotated result image will have. To do that it creates a matrix to represent rotation through the entered angle. It makes an array holding the corner points for the original image and then applies the matrix to rotate them. It then subtracts the minimum and maximum X and Y coordinates to get the rotated image's bounds.
The method then makes the rotated image. First it creates a new Bitmap with the new width and height. It then creates a Graphics object associated with that Bitmap.
The code uses three transformations to rotate the image. First it translates the original image so it is centered at the origin. Second it rotates the image through the desired angle. Third it translates the result so it is centered on the new Bitmap.
After it defines the Graphics object's transformations, it simply draws the original image onto the Graphics object.
The method finishes by calling SetPictureBoxSize to scale the result (described shortly) and then displays the result Bitmap in the PictureBox.
Scaling the Result
Displaying the result at different scales is relatively easy. The program displays it in a PictureBox with SizeMode set to Zoom. That makes the PictureBox scale its image so it is as large as possible without distorting it.
The PictureBox is contained in a Panel with AutoScroll set to true, so the Panel displays scroll bars if its contents (the PictureBox) won't fit.
When the program generates a rotated picture, it uses the following method to size the PictureBox appropriately.
// Set the PictureBox to display the image
// at the desired scale.
private void SetPictureBoxSize()
{
if (RotatedImage == null) return;
picRotatedImage.ClientSize = new Size(
(int)(RotatedImage.Width * ImageScale),
(int)(RotatedImage.Height * ImageScale));
}
This code sets the PictureBox control's ClientSize to be the scale factor times the size of the resulting image. The PictureBox sizes itself so it displays the image at the desired size and has room to display its margins. Because SizeMode is Zoom, it displays the image as large as possible within its client area, so the image is scaled as desired. If the PictureBox doesn't fit inside the Panel, the Panel provides scrolling.
The rest of the program performs general bookkeeping tasks such as loading and saving images, enabling the Save As menu item only when an image is loaded, and handling the Scale menu's menu items. Download the example and look at its code to see how it works.
(By the way, the sample image should be rotated about -2.3 degrees to straighten it out.)
Download the example to experiment with it and to see additional details.
|