Daniel Lip
Daniel Lip

Reputation: 11319

How can I rotate an image by any degree?

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

Upvotes: 15

Views: 49373

Answers (7)

VeniVidiArastis
VeniVidiArastis

Reputation: 31

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;
}

Upvotes: 3

Mustafa Doğan
Mustafa Doğan

Reputation: 9

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;
    }

Upvotes: 0

Omar
Omar

Reputation: 16623

I didn't understand what's your problem but I think that your code could be improved. I think that you don't need to use directly the Matrix class. There are some functions that does this work for you. Infact the only things you need are: set the point of the rotation as the center, rotate the graphics and draw on it, using some functions by the Graphics class. So to rotate an image you can use this simple code:

private Bitmap RotateImage(Bitmap bmp, float angle) {
     Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
     rotatedImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);

     using (Graphics g = Graphics.FromImage(rotatedImage)) {
        // Set the rotation point to the center in the matrix
        g.TranslateTransform(bmp.Width / 2, bmp.Height / 2);
        // Rotate
        g.RotateTransform(angle);
        // Restore rotation point in the matrix
        g.TranslateTransform(- bmp.Width / 2, - bmp.Height / 2);
        // Draw the image on the bitmap
        g.DrawImage(bmp, new Point(0, 0));
     }

     return rotatedImage;
}

Upvotes: 29

Timo Treichel
Timo Treichel

Reputation: 386

I tried the answer of @Omar myself and realized, that the original image gets cut at the sides ... i've rewritten it so it resizes the image to the new sizes:

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 = (float)(c1 * Math.Sin(alpha * Math.PI / 180) / Math.Sin(gamma * Math.PI / 180));
    float b1 = (float)(c1 * Math.Sin(beta * Math.PI / 180) / Math.Sin(gamma * Math.PI / 180));

    float c2 = bmp.Width;
    float a2 = (float)(c2 * Math.Sin(alpha * Math.PI / 180) / Math.Sin(gamma * Math.PI / 180));
    float b2 = (float)(c2 * Math.Sin(beta * Math.PI / 180) / Math.Sin(gamma * 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;
}

Changes of the Size at 30 Degree Radius

Upvotes: 7

Queequeg
Queequeg

Reputation: 131

Based on the previous answers I created this code that doesn't cut the image (the other examples were not working for me)

    private Bitmap RotateImage(Bitmap bmp, float angle)
    {
        float height = bmp.Height;
        float width = bmp.Width;
        int hypotenuse = System.Convert.ToInt32(System.Math.Floor(Math.Sqrt(height * height + width * width)));
        Bitmap rotatedImage = new Bitmap(hypotenuse, hypotenuse);
        using (Graphics g = Graphics.FromImage(rotatedImage))
        {
            g.TranslateTransform((float)rotatedImage.Width / 2, (float)rotatedImage.Height / 2); //set the rotation point as the center into the matrix
            g.RotateTransform(angle); //rotate
            g.TranslateTransform(-(float)rotatedImage.Width / 2, -(float)rotatedImage.Height / 2); //restore rotation point into the matrix
            g.DrawImage(bmp, (hypotenuse - width) / 2, (hypotenuse - height) / 2, width, height);
        }
        return rotatedImage;
    }

Upvotes: 9

gunakkoc
gunakkoc

Reputation: 1069

I was using this function in VB:

    Public Function RotateImage(ByRef image As Image, ByVal angle As Single) As Drawing.Bitmap
    If image Is Nothing Then
        Throw New ArgumentNullException("image")
    End If

    Dim pi2 As Single = Math.PI / 2.0
    Dim oldWidth As Single = image.Width
    Dim oldHeight As Single = image.Height

    Dim theta As Single = angle * Math.PI / 180.0
    Dim locked_theta As Single = theta

    If locked_theta < 0.0 Then locked_theta += 2 * Math.PI

    Dim newWidth, newHeight As Single
    Dim nWidth, nHeight As Integer

    Dim adjacentTop, oppositeTop As Single
    Dim adjacentBottom, oppositeBottom As Single

    If (locked_theta >= 0.0 And locked_theta < pi2) Or _
    (locked_theta >= Math.PI And locked_theta < (Math.PI + pi2)) Then
        adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth
        oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth

        adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight
        oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight
    Else
        adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight
        oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight

        adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth
        oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth
    End If



    newWidth = adjacentTop + oppositeBottom
    newHeight = adjacentBottom + oppositeTop

    nWidth = Int(Math.Ceiling(newWidth))
    nHeight = Int(Math.Ceiling(newHeight))

    Dim rotatedBmp As New Drawing.Bitmap(nWidth, nHeight)

    Dim g As Graphics = Graphics.FromImage(rotatedBmp)

    Dim points(2) As Point

    If (locked_theta >= 0.0 And locked_theta < pi2) Then

        points(0) = New Point(Int(oppositeBottom), 0)
        points(1) = New Point(nWidth, Int(oppositeTop))
        points(2) = New Point(0, Int(adjacentBottom))

    ElseIf locked_theta >= pi2 And locked_theta < Math.PI Then

        points(0) = New Point(nWidth, Int(oppositeTop))
        points(1) = New Point(Int(adjacentTop), nHeight)
        points(2) = New Point(Int(oppositeBottom), 0)

    ElseIf locked_theta >= Math.PI And locked_theta < (Math.PI + pi2) Then

        points(0) = New Point(Int(adjacentTop), nHeight)
        points(1) = New Point(0, Int(adjacentBottom))
        points(2) = New Point(nWidth, Int(oppositeTop))

    Else

        points(0) = New Point(0, Int(adjacentBottom))
        points(1) = New Point(Int(oppositeBottom), 0)
        points(2) = New Point(Int(adjacentTop), nHeight)
    End If

    g.DrawImage(image, points)

    g.Dispose()
    image.Dispose()

    Return rotatedBmp

End Function

Upvotes: 1

Rajesh
Rajesh

Reputation: 1469

Have you tried RotateFlip?

public partial class Form1 : Form
{
    Image myImage;
    AnimatedGif myGif;
    Bitmap bitmap;
    public Form1()
    {
        InitializeComponent();
        myImage = Image.FromFile(@"D:\fananimation.gif");
        bitmap = new Bitmap(myImage);
        bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
        this.pictureBox1.Image = bitmap;
    }

}

Source

Upvotes: 6

Related Questions