Rotate images in C#


[Rotate images]

This program uses the following RotateBitmap method to rotate an image.

// Return a bitmap rotated around its center.
private Bitmap RotateBitmap(Bitmap bm, float angle)
{
    // Make a Matrix to represent rotation
    // by this angle.
    Matrix rotate_at_origin = new Matrix();
    rotate_at_origin.Rotate(angle);

    // Rotate the image's corners to see how big
    // it will be after rotation.
    PointF[] points =
    {
        new PointF(0, 0),
        new PointF(bm.Width, 0),
        new PointF(bm.Width, bm.Height),
        new PointF(0, bm.Height),
    };
    rotate_at_origin.TransformPoints(points);
    float xmin, xmax, ymin, ymax;
    GetPointBounds(points, out xmin, out xmax,
        out ymin, out ymax);

    // Make a bitmap to hold the rotated result.
    int wid = (int)Math.Round(xmax - xmin);
    int hgt = (int)Math.Round(ymax - ymin);
    Bitmap result = new Bitmap(wid, hgt);

    // Create the real rotation transformation.
    Matrix rotate_at_center = new Matrix();
    rotate_at_center.RotateAt(angle,
        new PointF(wid / 2f, hgt / 2f));

    // Draw the image onto the new bitmap rotated.
    using (Graphics gr = Graphics.FromImage(result))
    {
        // Use smooth image interpolation.
        gr.InterpolationMode = InterpolationMode.High;

        // Clear with the color in the image's upper left corner.
        gr.Clear(bm.GetPixel(0, 0));

        //// For debugging. (It's easier to see the background.)
        //gr.Clear(Color.LightBlue);

        // Set up the transformation to rotate.
        gr.Transform = rotate_at_center;

        // Draw the image centered on the bitmap.
        int x = (wid - bm.Width) / 2;
        int y = (hgt - bm.Height) / 2;
        gr.DrawImage(bm, x, y);
    }
    
    // Return the result bitmap.
    return result;
}

When you rotate a rectangle, the result may be wider and taller than the original image because the rotated corners may stick out on the sides. This method returns a new image that is big enough to display all of the contents of the original image.

To get the dimensions of the rotated image, the code starts by creating a Matrix that rotates through the specified angle. It creates an array of PointF structures holding the bitmap’s corners and uses the Matrix to rotate the corners.

The method then calls the GetPointBounds method to get the bounds for the rotated corners. That method simply loops through the points and keeps track and the minimum and maximum X and Y values. That method is straightforward so it isn’t shown here.

Next the code creates a result bitmap big enough to hold the rotated image. It then creates a Matrix to represent rotation through the desired angle around the center of the result bitmap.

The method then creates a Graphics object associated with the result bitmap and sets that object’s InterpolationMode property to produce a smooth result. The code clears the result bitmap with whatever color is in the original image’s upper left corner. (The commented out debugging code fills the result bitmap with light blue so it’s easy to see where the original image is drawn.)

Next the code sets the Graphics object’s Transform property so it rotates anything it draws. Finally the method draws the original bitmap at a position that makes it centered on the result bitmap. The transform automatically rotates the image.

The rest of the program just lets you open an image file and displays the rotated result. Download the example to see the details.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, graphics, image processing, transformations and tagged , , , , , , , , , . Bookmark the permalink.

5 Responses to Rotate images in C#

  1. Pingback: Interactively rotate images by an arbitrary angle in C# - C# HelperC# Helper

  2. Anonymous says:

    GetPointBounds is straightforward *to you*. But you also don’t need to read your own tutorial.

    You do need to learn we dont’ wanna tell you our name and email and website to make this comment.

    • RodStephens says:

      GetPointBounds is straightforward *to you*. But you also don’t need to read your own tutorial.

      I often don’t include every bit of code to prevent posts from getting unnecessarily long. (And some of them would be really long.) You can (and I encourage you to) download the example to see every last detail.

      Here’s the description in the post:

      That method simply loops through the points and keeps track and the minimum and maximum X and Y values.

      Here’s the method’s code:

      // Find the bounding rectangle for an array of points.
      private void GetPointBounds(PointF[] points,
          out float xmin, out float xmax,
          out float ymin, out float ymax)
      {
          xmin = points[0].X;
          xmax = xmin;
          ymin = points[0].Y;
          ymax = ymin;
          foreach (PointF point in points)
          {
              if (xmin > point.X) xmin = point.X;
              if (xmax < point.X) xmax = point.X;
              if (ymin > point.Y) ymin = point.Y;
              if (ymax < point.Y) ymax = point.Y;
          }
      }

      Alternatively you could use LINQ. For example, see Compare LINQ speeds for different approaches in C#.

      You do need to learn we dont’ wanna tell you our name and email and website to make this comment.

      This blog uses the WordPress tool provided by my ISP. I have no control over how it works or the fact that it's the only tool available. As you noticed, you can just make up a name and email address.

  3. Rob says:

    Many thanks for this. I was about to go down the path of calculating all the trig manually. this way is much simpler!

  4. Rob says:

    one thing to look out for – gr.DrawImage(bm, x, y) will sometimes draw the image scaled as well as rotated. I think it’s due to the resolution of the image being different to the display dpi. this is easily fixed by using DrawImage(img, Rectangle) overload using the width & height of the original un-rotated bitmap

Leave a Reply

Your email address will not be published. Required fields are marked *