Search code examples
c#graphics

Rotate a Graphics bitmap at its center


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?


Solution

  • 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+ xPos;   
    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..