Как я могу повернуть изображение на любую степень?
У меня есть анимированный GIF и я использую класс для анализа изображений (кадров) из него. Класс это:
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; } }
}
Теперь в form1 я зацикливаюсь на кадрах в этом случае 4, и я хочу повернуть анимацию на любую степень. Теперь его вращают каждые 45 или 90 градусов. Я хочу добавить больше кадров (изображений) в анимацию, поэтому, если я установлю вращение на 31 или на 10 градусов, я увижу анимацию, вращающуюся на 10 градусов.
Это код в Form1, который не работает хорошо. Я использую функцию вращения, которую я еще не проверял, работает ли она.
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;
}
}
}
Анимированные картинки, которые я использую для теста, можно найти здесь:
7 ответов
Я не понял, в чем твоя проблема, но я думаю, что твой код можно улучшить. Я думаю, что вам не нужно использовать непосредственно Matrix
учебный класс. Есть некоторые функции, которые делают эту работу за вас. Фактически, единственное, что вам нужно: установить точку поворота в качестве центра, повернуть графику и нарисовать ее, используя некоторые функции Graphics
учебный класс. Таким образом, чтобы повернуть изображение, вы можете использовать этот простой код:
private Bitmap RotateImage(Bitmap bmp, float angle) {
Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
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;
}
Основываясь на предыдущих ответах, я создал этот код, который не вырезал изображение (другие примеры не работали для меня)
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;
}
Я сам попробовал ответ Омара Махили и понял, что оригинальное изображение обрезается по бокам... Я переписал его, чтобы оно изменило размеры изображения до новых размеров:
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;
}
Вы пробовали 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;
}
}
Я использовал эту функцию в 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
Я проверил ответы, и все они имеют по крайней мере одну из следующих проблем:
- Обрезка/неправильное центрирование
- Ненужная маржа
- Ошибки с некоторыми диапазонами углов
- Излишне сложные расчеты/код
Это решение может обрабатывать любой угол (положительный, отрицательный, более 360° и т. д.). Нет кадрирования или чрезмерных полей. Утечек памяти тоже нет.
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;
}
В зависимости от кода Тимо я внес некоторые улучшения, с улучшением отрицательные углы (до -360) могут быть успешно указаны в качестве параметра.
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;
}