I am trying to use the combination of the timer class and a codeproject to rotate an image smoothly in a picture box control (Visual Studio 2010 C#). The problem I am having is either the picture doesn't rotate or I get a threading exception. Something about "the object is in use elsewhere". Here's the main parts of the code, I would greatly appreciate any help you can provide. Thank you.
private void Form1_Load(object sender, EventArgs e)
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
}
public static Bitmap RotateImage(Image image, float angle)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
bool bNoClip = false;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float angle, bool bNoClip)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle, bool bNoClip)
{
int W, H, X, Y;
if (bNoClip)
{
double dW = (double)image.Width;
double dH = (double)image.Height;
double degrees = Math.Abs(angle);
if (degrees <= 90)
{
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dH * dSin + dW * dCos);
H = (int)(dW * dSin + dH * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
else
{
degrees -= 90;
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dW * dSin + dH * dCos);
H = (int)(dH * dSin + dW * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
}
else
{
W = image.Width;
H = image.Height;
X = 0;
Y = 0;
}
//create a new empty bitmap to hold rotated image
Bitmap bmpRet = new Bitmap(W, H);
bmpRet.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(bmpRet);
//Put the rotation point in the "center" of the image
g.TranslateTransform(rotateAtX + X, rotateAtY + Y);
//rotate the image
g.RotateTransform(angle);
//move the image back
g.TranslateTransform(-rotateAtX - X, -rotateAtY - Y);
//draw passed in image onto graphics object
g.DrawImage(image, new PointF(0 + X, 0 + Y));
return bmpRet;
}
I think you should invalidate and paint in you OnPaint or OnDraw event
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
// this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
pictureBox1.Invalidate();
}
In your form you should enable this styles for a better perfomance and smoothness
SetStyle( ControlStyles.ResizeRedraw, true );
SetStyle( ControlStyles.UserPaint, true );
SetStyle( ControlStyles.AllPaintingInWmPaint, true );
SetStyle( ControlStyles.OptimizedDoubleBuffer, true );
Related
I need to draw a Fermat spiral in C#. I did it, but I want my drawing to be filled in PictureBox, no matter how big real size is.
public void DrawSpiral(double delta, double numCycles, int oX, int oY, SpiralType spiralType, Color color, Graphics g)
{
double a = Convert.ToInt32(textBox1.Text);
Pen p = new Pen(color, 1);
double prevX = oX;
double prevY = oY;
double X = oX;
double Y = oY;
double fi = Convert.ToInt32(textBox2.Text);
double radius = 0;
while (fi <= (numCycles * 360))
{
fi += delta;
if (spiralType == SpiralType.FermaPlus)
{
radius = a * Math.Sqrt(fi);
}
else if (spiralType == SpiralType.FermaMinus)
{
radius = -a * Math.Sqrt(fi);
}
prevX = X;
prevY = Y;
X = (radius * Math.Cos(fi / 180 * Math.PI)) + oX;
Y = (radius * Math.Sin(fi / 180 * Math.PI)) + oY;
g.DrawLine(p, (float)prevX, (float)prevY, (float)X, (float)Y);
}
}
private void DrawButton_Click(object sender, EventArgs e)
{
pictureBox1.Refresh();
Graphics g = pictureBox1.CreateGraphics();
DrawSpiral(2, 5, 150, 150, SpiralType.FermaPlus, Color.Blue, g);
DrawSpiral(2, 5, 150, 150, SpiralType.FermaMinus, Color.Red, g);
}
So, what should I do to have my drawing to be full filled in the PictureBox.
Here is one way to do it:
Change the signature of the DrawSpiral to include the ClientSize of the PictureBox instead of some center coordinates:
public void DrawSpiral(double delta, double numCycles, int spiralType,
Color color, Graphics g, Size sz)
Then calculate the center dynamically:
int oX = sz.Width / 2;
int oY = sz.Height / 2;
double prevX = oX;
double prevY = oY;
double X = oX;
double Y = oY;
Next calculate the factor a :
a = sz.Width / 2 / Math.Sqrt( numCycles * 360);
Finally call the method only from the Paint event, passing out the valid Graphics object:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Size sz = pictureBox1.ClientSize;
DrawSpiral(2, 5, SpiralType.FermaPlus, Color.Blue, g, sz);
DrawSpiral(2, 5, SpiralType.FermaMinus, Color.Red, g, sz);
}
Upon resizing the PictureBox it will still fill the area with the same number of loops..:
A few notes:
The quality and performance could be improved by first collecting the data in a List<Point> pointsand then using DrawLines(pen, points.ToArray())
I used just the width when I calculated the factor a. Use Math.Min(sz.Width, sz.Height) to always fit it into a non-square box!
I left your offset calculation in place; but you could instead do a g.TranslateTransform()..
The PictureBox will Invalidate/Refresh itself upon resizing. If you change any parameters do call Invalidate to pick them up!
I am working on a project for school, we need to make a basic top down race game in C# without using XNA.
First of all let me tell you that the stuff we have learned about programming so far has little to do with making something that even remotely looks like a racegame. It didn't get any more difficult than array's, loops etc.
So we didn't learn about graphics or anything like that.
Having said all that I am having the following problem.
We have created a Graphics object, and then use DrawImage and use a bitmap from a car.jpg.
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);
Then we wait for a key press e.g Right
case Keys.Right:
if (angle != 360)
{
angle += 10;
}
else
{
angle = 0;
}
this.Refresh();
break;
The problem we have is that the pivot point for the rotation is in the top left corner. So as soon as we move the car to something like (20,25) and start to rotate it, it will use (0,0) as the center of rotation. What we want to achieve is to have the center point of rotation at the center of our car.
We have tried looking for ways to change the centerX and centerY of the RotateTransform but have come to the conclusion that this isn't possible with the bitmap.
We have been struggling with this problem for over 2 days and can't seem to find any solution for achieving the thing we want.
Is there something we are doing wrong creating the Graphics object, or is there a totally different way to change centerX and centerY for the car?
To draw a rotated Bitmap you need to do a few steps to prepare the Graphics object:
first you move its origin onto the midpoint of the rotation
then you rotate by the desired angle
next you move it back
now you can draw the Bitmap
finally you reset the Graphics
This needs to be done for each bitmap.
Here are the steps in code to draw a Bitmap bmp at position (xPos, yPos):
float moveX = bmp.Width / 2f + xPos;
float moveY = bmp.Height / 2f+ xPosf;
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);
e.Graphics.ResetTransform();
There is one possible complication: If your Bitmap has different dpi resolution than the screen i.e. than the Graphics you must first adapt the Bitmap's dpi setting!
To adapt the Bitmapto the usual 96dpi you can simply do a
bmp.SetResolution(96,96);
To be prepared for future retina-like displays you can create a class variable you set at startup:
int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)
{
using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;
}
and use it after loading the Bitmap:
bmp.SetResolution(ScreenDpi , ScreenDpi );
As usual the DrawImage method uses the top left corner of the Bitmap. You may need to use different Points for the rotation point and possibly also for the virtual position of your car, maybe in the middle of its front..
Here is static class which will paint the image in desired location within desired area. Change the rotationangle value to rotate the image. And you can also pan and zoom the image.
Add this class in your Project and call the static functions from Win Form.
public static class FullImage
{
public static Image image;
public static RectangleF DisplayRect, SourceRect;
public static Size ParentBoundry;
public static float rotationangle=0;
internal static void Paint(Graphics graphics)
{
if (image == null)
return;
float hw = DisplayRect.X + DisplayRect.Width / 2f;
float hh = DisplayRect.Y + DisplayRect.Height / 2f;
System.Drawing.Drawing2D.Matrix m = graphics.Transform;
m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
graphics.Transform = m;
graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
graphics.ResetTransform();
}
public static void LoadImage(Image img)
{
image = img;
SizeF s = GetResizedSize(image, ParentBoundry);
SourceRect = new RectangleF(0, 0, image.Width, image.Height);
DisplayRect = new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
}
public static Size GetResizedSize(Image ImageToResize, Size size)
{
int sourceWidth = ImageToResize.Width;
int sourceHeight = ImageToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
return new Size(destWidth, destHeight);
}
internal static void MouseWheel(int delta)
{
if (delta > 0)
DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
else
DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);
}
private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
{
/// Original Size and Location
SizeF OriginalSize = ImageRectangle.Size;
PointF OriginalPoint = ImageRectangle.Location;
///Mouse cursor location -located in width% and height% of totaloriginal image
float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;
///Zoomed Image by scalefactor
SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
if (FinalSize.Width < 15 || FinalSize.Height < 15)
return ImageRectangle;
if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
return ImageRectangle;
/// How much width increases and height increases
float widhtincrease = FinalSize.Width - OriginalSize.Width;
float heightincrease = FinalSize.Height - OriginalSize.Height;
/// Adjusting Image location after zooming the image
PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);
return ImageRectangle;
}
static bool drag = false;
static Point Initial, CurrentMouse;
internal static void MouseMove(Point location)
{
CurrentMouse = location;
if (drag)
{
DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
Initial = location;
}
}
internal static void MouseDown(Point location)
{
Initial = location;
drag = true;
}
internal static void MouseUp(Point location)
{
drag = false;
}
}
After Adding this code in your project (Better add in separate cs file), Call the functions from Win Form class (Form1.cs).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
FullImage.ParentBoundry = new Size(this.Width, this.Height);
// Enter the image path
FullImage.LoadImage(Image.FromFile(#"D:\a.jpg"));
}
//Create a paint event
private void Form1_Paint(object sender, PaintEventArgs e)
{
FullImage.Paint(e.Graphics);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseDown(e.Location);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseMove(e.Location);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseUp(e.Location);
this.Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Vault.FullImage.MouseWheel(e.Delta);
this.Invalidate();
}
Now, if you want to rotate the image, just set the value however you want (with slider, button or add some more functions to detect the mouse movement and then rotate)
Example: add a button and each time the button clicked increase the value by 1.
private void button1_clicked(object sender, EventArgs e)
{
FullImage.rotationangle++;
this.invalidate();
}
To rotate the top left from the center you first need to know the angle of it then adjust it by the angle you want and re-calculate the new top left by the new angle:
var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);
I'm using windows forms chart control and this is the wheel mouse event:
void chart1_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta < 0)
{
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
}
if (e.Delta > 0)
{
double xMin = chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum;
double xMax = chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum;
double yMin = chart1.ChartAreas[0].AxisY.ScaleView.ViewMinimum;
double yMax = chart1.ChartAreas[0].AxisY.ScaleView.ViewMaximum;
double posXStart = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.Location.X) - (xMax - xMin) / 4;
double posXFinish = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.Location.X) + (xMax - xMin) / 4;
double posYStart = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Location.Y) - (yMax - yMin) / 4;
double posYFinish = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Location.Y) + (yMax - yMin) / 4;
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(posXStart, posXFinish);
chart1.ChartAreas[0].AxisY.ScaleView.Zoom(posYStart, posYFinish);
}
}
The problem is in the chart paint event when I draw points and connect them with lines when I use the wheel the points and lines are gone I don't think they are scaling right.
Pen pen = new Pen(Color.Blue, 2.5f);
SolidBrush myBrush = new SolidBrush(Color.Red);
private void chart1_Paint(object sender, PaintEventArgs e)
{
if (paintToCalaculate)
{
Series s = chart1.Series.FindByName("dummy");
if (s == null) s = chart1.Series.Add("dummy");
drawPoints.Clear();
s.Points.Clear();
foreach (PointF p in valuePoints)
{
s.Points.AddXY(p.X, p.Y);
DataPoint pt = s.Points[0];
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(pt.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(pt.YValues[0]);
drawPoints.Add(new Point((int)x, (int)y));
s.Points.Clear();
}
paintToCalaculate = false;
chart1.Series.Remove(s);
}
foreach (Point p in drawPoints)
{
e.Graphics.FillEllipse(Brushes.Red, p.X - 2, p.Y - 2, 4, 4);
}
if (drawPoints.Count > 1)
{
e.Graphics.DrawLines(pen, drawPoints.ToArray());
}
}
How can I take care in the wheel mouse event also about the points and lines I draw in the paint event ?
I think you may need to set your 'paintToCalaculate' flag when zooming. It gets turned off during the first paint event and is not turned back on when zooming, so your lines are not being rescaled.
how to rotate image through put in angle value in textbox using graphics in asp.net c#?
I take a textbox and image and button.I want to give value in angle in textbox and image will be rotate according to given value.so,i can only doing a simple button_click event ,but it is not proper for textbox.so,give simple code ...
protected void Button2_Click(object sender, EventArgs e)
{
string path = Server.MapPath(Image1.ImageUrl);
////create an image object from the image in that path
System.Drawing.Image img = System.Drawing.Image.FromFile(path);
////rotate the image
img.RotateFlip(RotateFlipType.Rotate90FlipXY);
////save the image out to the file
img.Save(path);
////release image file
img.Dispose();
}
Following code save the rotated image into newfile.png in current project folder path
Try This:
protected void Button2_Click1(object sender, EventArgs e)
{
string path = Server.MapPath(Image1.ImageUrl);
string newpath =Server.MapPath(#"~/filename.png");
////create an image object from the image in that path
System.Drawing.Bitmap img =new System.Drawing.Bitmap(path);
Bitmap map = RotateBitmap(COnvert.ToSingle(textBox1.Text), img);
map.Save(newpath);
////release image file
img.Dispose();
}
public Bitmap RotateBitmap(float Angle, Bitmap bm_in)
{
try
{
float wid = bm_in.Width;
float hgt = bm_in.Height;
Point[] corners = { new Point(0, 0), new Point(int.Parse(wid.ToString()), 0), new Point(0, int.Parse(hgt.ToString())), new Point(int.Parse(wid.ToString()), int.Parse(hgt.ToString())) };
int cx = int.Parse(wid.ToString()) / 2;
int cy = int.Parse(hgt.ToString()) / 2;
long i;
for (i = 0; i <= 3; i++)
{
corners[i].X -= Convert.ToInt32(cx.ToString());
corners[i].Y -= Convert.ToInt32(cy.ToString());
}
float theta = (float)(Angle * Math.PI / 180.0);
float sin_theta = (float)Math.Sin(theta);
float cos_theta = (float)Math.Cos(theta);
float X;
float Y;
for (i = 0; i <= 3; i++)
{
X = corners[i].X;
Y = corners[i].Y;
corners[i].X = (int)(X * cos_theta + Y * sin_theta);
corners[i].Y = (int)(-X * sin_theta + Y * cos_theta);
}
float xmin = corners[0].X;
float ymin = corners[0].Y;
for (i = 1; i <= 3; i++)
{
if (xmin > corners[i].X)
xmin = corners[i].X;
if (ymin > corners[i].Y)
ymin = corners[i].Y;
}
for (i = 0; i <= 3; i++)
{
corners[i].X -= int.Parse(xmin.ToString());
corners[i].Y -= int.Parse(ymin.ToString());
}
Bitmap bm_out = new Bitmap((int)(-2 * xmin), (int)(-2 * ymin));
Graphics gr_out = Graphics.FromImage(bm_out);
// ERROR: Not supported in C#: ReDimStatement
Point[] temp = new Point[3];
if (corners != null)
{
Array.Copy(corners, temp, Math.Min(corners.Length, temp.Length));
}
corners = temp;
gr_out.DrawImage(bm_in, corners);
return bm_out;
}
catch (Exception ex)
{
string s = ex.Message;
return bm_in;
}
}
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:
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;
}
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;
}
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;
}
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
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
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;
}