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:
17 Answers
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;
} 5 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; } 3 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;
} 7 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; }
} 1 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;
} 2 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 1 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; }