How can I rotate an image by any degree?

Viewed 40654

I have animated gif and I'm using a class to parse the images(frames) from it. The class is:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.IO;

public class AnimatedGif
{
    private List<AnimatedGifFrame> mImages = new List<AnimatedGifFrame>();
    public AnimatedGif(string path)
    {
        Image img = Image.FromFile(path);
        int frames = img.GetFrameCount(FrameDimension.Time);
        if (frames <= 1) throw new ArgumentException("Image not animated");
        byte[] times = img.GetPropertyItem(0x5100).Value;
        int frame = 0;
        for (; ; )
        {
            int dur = BitConverter.ToInt32(times, 4 * frame);
            mImages.Add(new AnimatedGifFrame(new Bitmap(img), dur));
            if (++frame >= frames) break;
            img.SelectActiveFrame(FrameDimension.Time, frame);
        }
        img.Dispose();
    }
    public List<AnimatedGifFrame> Images { get { return mImages; } }
}

public class AnimatedGifFrame
{
    private int mDuration;
    private Image mImage;
    internal AnimatedGifFrame(Image img, int duration)
    {
        mImage = img; mDuration = duration;
    }
    public Image Image { get { return mImage; } }
    public int Duration { get { return mDuration; } }
}

Now in form1 I loop over the frames in this case 4 and I want to rotate the animation by any degree. Now its rotating each 45 or 90 degrees. I want to add more frames(images) to the animation so if I set the rotation to 31 or to 10 degrees so I will see the animation rotating in 10 degrees.

This is the code in Form1 which is not working good. I'm using a function for the rotation which I didn't test yet if its any working.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace AnimatedGifEditor
{
    public partial class Form1 : Form
    {
        Image myImage;
        AnimatedGif myGif;
        Bitmap bitmap;

        public Form1()
        {
            InitializeComponent();

            myImage = Image.FromFile(@"D:\fananimation.gif");
            myGif = new AnimatedGif(@"D:\fananimation.gif");
            for (int i = 0; i < myGif.Images.Count; i++)
            {
                pictureBox1.Image = myGif.Images[3].Image;
                bitmap = new Bitmap(pictureBox1.Image);
                rotateImage(bitmap, 76);
                pictureBox1.Image = bitmap;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

private Bitmap RotateImg(Bitmap bmp, float angle, Color bkColor)
    {
        int w = bmp.Width;
        int h = bmp.Height;
        bmp.PixelFormat pf = default(bmp.PixelFormat);
        if (bkColor == Color.Transparent)
        {
            pf = bmp.Format32bppArgb;
        }
        else
        {
            pf = bmp.PixelFormat;
        }

        Bitmap tempImg = new Bitmap(w, h, pf);
        Graphics g = Graphics.FromImage(tempImg);
        g.Clear(bkColor);
        g.DrawImageUnscaled(bmp, 1, 1);
        g.Dispose();

        GraphicsPath path = new GraphicsPath();
        path.AddRectangle(new RectangleF(0f, 0f, w, h));
        Matrix mtrx = new Matrix();
        //Using System.Drawing.Drawing2D.Matrix class 
        mtrx.Rotate(angle);
        RectangleF rct = path.GetBounds(mtrx);
        Bitmap newImg = new Bitmap(Convert.ToInt32(rct.Width), Convert.ToInt32(rct.Height), pf);
        g = Graphics.FromImage(newImg);
        g.Clear(bkColor);
        g.TranslateTransform(-rct.X, -rct.Y);
        g.RotateTransform(angle);
        g.InterpolationMode = InterpolationMode.HighQualityBilinear;
        g.DrawImageUnscaled(tempImg, 0, 0);
        g.Dispose();
        tempImg.Dispose();
        return newImg;
    }
    }
}

The animated gif I'm using for the test can be found here:

enter image description here

7 Answers

I checked the answers and they all have at least one of the following problems:

  • Cropping/incorrect centering
  • Unnecessary margin
  • Errors with some angle ranges
  • Unnecessarily complicated calculations/code

This solution can handle any angle (positive, negative, over 360° etc.). There is no cropping or excessive margins. No memory leaks either.

public Bitmap RotateBitmap(Bitmap bmp, float angle)
{
    double radianAngle = angle / 180.0 * Math.PI;
    double cosA = Math.Abs(Math.Cos(radianAngle));
    double sinA = Math.Abs(Math.Sin(radianAngle));

    int newWidth = (int)(cosA * bmp.Width + sinA * bmp.Height);
    int newHeight = (int)(cosA * bmp.Height + sinA * bmp.Width);

    var rotatedBitmap = new Bitmap(newWidth, newHeight);
    rotatedBitmap.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
    
    using (Graphics g = Graphics.FromImage(rotatedBitmap))
    {
        g.TranslateTransform(rotatedBitmap.Width / 2, rotatedBitmap.Height / 2);
        g.RotateTransform(angle);
        g.TranslateTransform(-bmp.Width / 2, -bmp.Height / 2);
        g.DrawImage(bmp, new Point(0, 0));
    }

    bmp.Dispose();//Remove if you want to keep oryginal bitmap

    return rotatedBitmap;
}

Depending on Timo's code, i made some improvements, with improvement, negative angles (up to -360) can be give as a parameter succesfully

private static Bitmap RotateImage(Bitmap bmp, float angle) 
    {
        float alpha = angle;

        //edit: negative angle +360
        while (alpha < 0) alpha += 360;

        float gamma = 90;
        float beta = 180 - angle - gamma;

        float c1 = bmp.Height;
        float a1 = Math.Abs((float)(c1 * Math.Sin(alpha * Math.PI / 180)));
        float b1 = Math.Abs((float)(c1 * Math.Sin(beta * Math.PI / 180)));

        float c2 = bmp.Width;
        float a2 = Math.Abs((float)(c2 * Math.Sin(alpha * Math.PI / 180)));
        float b2 = Math.Abs((float)(c2 * Math.Sin(beta * Math.PI / 180)));

        int width = Convert.ToInt32(b2 + a1);
        int height = Convert.ToInt32(b1 + a2);

        Bitmap rotatedImage = new Bitmap(width, height);
        using (Graphics g = Graphics.FromImage(rotatedImage))
        {
            g.TranslateTransform(rotatedImage.Width / 2, rotatedImage.Height / 2); //set the rotation point as the center into the matrix
            g.RotateTransform(angle); //rotate
            g.TranslateTransform(-rotatedImage.Width / 2, -rotatedImage.Height / 2); //restore rotation point into the matrix
            g.DrawImage(bmp, new Point((width - bmp.Width) / 2, (height - bmp.Height) / 2)); //draw the image on the new bitmap
        }
        return rotatedImage;
    }
Related