Rotate a Graphics bitmap at its center
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 Bitmap
to 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..
How can I rotate a bitmap, keeping its scale
Answered on MSDN:
static void RotateBitmap2( string srcFilename, string destFilename, double degrees )
{
using( var srcBitmap = Bitmap.FromFile( srcFilename ) )
{
using( var g1 = Graphics.FromImage( srcBitmap ) )
{
var w = srcBitmap.Width;
var h = srcBitmap.Height;
g1.TranslateTransform( w / 2, h / 2 );
g1.RotateTransform( checked((float)degrees) );
g1.TranslateTransform( -w / 2, -h / 2 );
var a = new[] { new Point( 0, 0 ), new Point( w, 0 ), new Point( w, h ), new Point( 0, h ) };
g1.TransformPoints( CoordinateSpace.Device, CoordinateSpace.World, a );
int new_w = a.Max( p => p.X ) - a.Min( p => p.X );
int new_h = a.Max( p => p.Y ) - a.Min( p => p.Y );
using( var rotatedImage = new Bitmap( new_w, new_h, g1 ) )
{
using(var g2 = Graphics.FromImage(rotatedImage))
{
g2.TranslateTransform( new_w / 2, new_h / 2 );
g2.RotateTransform( checked((float)degrees) );
g2.DrawImageUnscaled( srcBitmap, -w / 2, -h / 2 );
}
rotatedImage.Save( destFilename, ImageFormat.Png );
}
}
}
}
Android rotate bitmap around center without resizing
This worked for me!
I created a method that returns a matrix. The matrix can be used in the following drawing method:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)
Here you go! (The parameter shape can be replaced with ease, if you would like that, just leave a comment):
public static Matrix rotateMatrix(Bitmap bitmap, Shape shape, int rotation) {
float scaleWidth = ((float) shape.getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) shape.getHeight()) / bitmap.getHeight();
Matrix rotateMatrix = new Matrix();
rotateMatrix.postScale(scaleWidth, scaleHeight);
rotateMatrix.postRotate(rotation, shape.getWidth()/2, shape.getHeight()/2);
rotateMatrix.postTranslate(shape.getX(), shape.getY());
return rotateMatrix;
}
NB: If you want an animated rotation, the rotation parameter will have to be updated with new values every frame eg. 1 then 2 then 3 ...
Properly rotate an image
The issue occurs in the rotating is related to the bounding box. It is clipping the edge because of the image you provided does not fit into the area that you have given.
I also faced this issue. So I tried a solution from here.
Adding the code that works for me.
public static Bitmap RotateImageN(Bitmap bitmap, float angle)
{
Matrix matrix = new Matrix();
matrix.Translate(bitmap.Width / -2, bitmap.Height / -2, MatrixOrder.Append);
matrix.RotateAt(angle, new System.Drawing.Point(0, 0), MatrixOrder.Append);
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.AddPolygon(new System.Drawing.Point[] { new System.Drawing.Point(0, 0), new System.Drawing.Point(bitmap.Width, 0), new System.Drawing.Point(0, bitmap.Height) });
graphicsPath.Transform(matrix);
System.Drawing.PointF[] points = graphicsPath.PathPoints;
Rectangle rectangle = boundingBox(bitmap, matrix);
Bitmap resultBitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics gDest = Graphics.FromImage(resultBitmap))
{
Matrix mDest = new Matrix();
mDest.Translate(resultBitmap.Width / 2, resultBitmap.Height / 2, MatrixOrder.Append);
gDest.Transform = mDest;
gDest.DrawImage(bitmap, points);
return resultBitmap;
}
}
}
private static Rectangle boundingBox(Image image, Matrix matrix)
{
GraphicsUnit graphicsUnit = new GraphicsUnit();
Rectangle boundingRectangle = Rectangle.Round(image.GetBounds(ref graphicsUnit));
Point topLeft = new Point(boundingRectangle.Left, boundingRectangle.Top);
Point topRight = new Point(boundingRectangle.Right, boundingRectangle.Top);
Point bottomRight = new Point(boundingRectangle.Right, boundingRectangle.Bottom);
Point bottomLeft = new Point(boundingRectangle.Left, boundingRectangle.Bottom);
Point[] points = new Point[] { topLeft, topRight, bottomRight, bottomLeft };
GraphicsPath graphicsPath = new GraphicsPath(points, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line });
graphicsPath.Transform(matrix);
return Rectangle.Round(graphicsPath.GetBounds());
}
VB.NET Rotate graphics object around center
I started a new VB.NET Windows Forms project. I added a Panel of 200px x 200px and a Button to pause the animation as desired. I gave Panel1 a background image:
Made an image a little like yours:
and used the following code:
Public Class Form1
Dim wiggle As Bitmap
Dim tim As Timer
Sub MoveWiggle(sender As Object, e As EventArgs)
Static rot As Integer = 0
Panel1.Refresh()
Using g = Panel1.CreateGraphics()
Using fnt As New Font("Consolas", 12), brsh As New SolidBrush(Color.Red)
' the text will not be rotated or translated
g.DrawString($"{rot}°", fnt, brsh, New Point(10, 10))
End Using
' the image will be rotated and translated
g.TranslateTransform(100, 100)
g.RotateTransform(CSng(rot))
g.DrawImage(wiggle, -80, 0)
End Using
rot = (rot + 10) Mod 360
End Sub
Private Sub bnPause_Click(sender As Object, e As EventArgs) Handles bnPause.Click
Static isPaused As Boolean = False
If isPaused Then
tim.Start()
bnPause.Text = "Pause"
Else
tim.Stop()
bnPause.Text = "Start"
End If
isPaused = Not isPaused
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
wiggle = New Bitmap("C:\temp\path3494.png")
wiggle.SetResolution(96, 96) ' my image had a strange resolution
tim = New Timer With {.Interval = 50}
AddHandler tim.Tick, AddressOf MoveWiggle
tim.Start()
End Sub
Private Sub Form1_Closing(sender As Object, e As EventArgs) Handles MyBase.Closing
RemoveHandler tim.Tick, AddressOf MoveWiggle
tim.Dispose()
wiggle.Dispose()
End Sub
End Class
and achieved this:
Note 1: It is important to set the transformations in the correct order.
Note 2: I called .Dispose()
on the disposable resources in the MyBase.Closing
event. This makes sure that memory is left clean and nothing leaks.
There are undoubtedly better ways to create animations, but at your desired one frame per second this achieves the effect you're after.
Related Topics
Serializable Classes and Dynamic Proxies in Ef - How
JSONconvert.Deserializer Indexing Issues
Overriding Fields or Properties in Subclasses
Why Is There a Difference in Checking Null Against a Value in Vb.Net and C#
C# Windows Form .Net and Dos Console
How to Access Session in a Webmethod
Difference Between Select and Convertall in C#
Converting Integers to Roman Numerals
Use Local Images in Webbrowser Control
How to Apply a General Rule for Remapping All Property Names When Serializing with JSON.Net
Convert Data Type from Inherited Classes in C#
Delete a Single Record from Entity Framework
Rsa Encryption, Getting Bad Length
Read a Xml (From a String) and Get Some Fields - Problems Reading Xml