All the three functions expect the angle of rotation to be in radians. If
you have degrees to begin with you can convert from degrees to radians by using
the formula
radian = (2*pi *degree)/360
Here are the steps that we take to rotate the bitmap.
1.
Create a couple of device contexts
compatible with the display. One of them will be used to hold the source bitmap
and the other will hold the destination bitmap (for the rotated image).
2.
Precompute the cosine and the sine of the
angle. This will help save a few microseconds in subsequent computations.
3.
Compute the bounding rectangle of the
rotated image. We use the formula
newx = x.cos(angle) + y.sin(angle)
newy = y.cos(angle) - x.sin(angle)
We assume one of the corners (0,0) to be the center of rotation and therefore
need to calculate the new co-ordinates of the other three corners. Based on
this we can determine the widht and height of the new bitmap.
4.
Since the rotated image will not occupy
the entire area of the new bitmap, we fill the destination bitmap with the
background color specified through the function argument.
5.
Since we will use NTs support for linear
transformation we set the graphic mode of the destination DC to support this.
6.
We set up the XFORM struction and call
SetWorldTransform() to activate the transformation. The SetWorldTransform()
function sets up the stage for BitBlt(), which is when the transformation takes
place. The algorithm used for the linear transformation is
newx = x * eM11 + y * eM21 + eDx
newy = x * eM12 + y * eM22 + eDy
For rotation eM11 and eM22 should be the cosine of the rotation angle, eM12
should be the sine of the rotation angle and eM21 should be -eM12. In the DC we
are using, since the +ve y direction is downwards, we reverse the signs of eM12
and eM21. We also set the translation components (eDx & eDy) so that
rotated image fits inside the new bitmap without part of it getting clipped.
7.
We finally call BitBlt() to do the actual
rotation. This call in itself looks like it will simply copy the image.
However, the previous call to SetWorldTransform() causes the image to be
rotated.
// GetRotatedBitmapNT - Create a new bitmap with rotated image
// Returns - Returns new bitmap with rotated image
// hBitmap - Bitmap to rotate
// radians - Angle of rotation in radians
// clrBack - Color of pixels in the resulting bitmap that do
// not get covered by source pixels
HBITMAP GetRotatedBitmapNT( HBITMAP
hBitmap, float
radians, COLORREF clrBack )
{
//
Create a memory DC compatible with the display
CDC
sourceDC, destDC;
sourceDC.CreateCompatibleDC(
NULL );
destDC.CreateCompatibleDC(
NULL );
//
Get logical coordinates
BITMAP
bm;
::GetObject(
hBitmap, sizeof( bm
), &bm );
float cosine = (float)cos(radians);
float sine = (float)sin(radians);
//
Compute dimensions of the resulting bitmap
//
First get the coordinates of the 3 corners other than origin
int x1 = (int)(bm.bmHeight * sine);
int y1 = (int)(bm.bmHeight * cosine);
int x2 = (int)(bm.bmWidth * cosine + bm.bmHeight *
sine);
int y2 = (int)(bm.bmHeight * cosine - bm.bmWidth *
sine);
int x3 = (int)(bm.bmWidth * cosine);
int y3 = (int)(-bm.bmWidth * sine);
int minx = min(0,min(x1, min(x2,x3)));
int miny = min(0,min(y1, min(y2,y3)));
int maxx = max(0,max(x1, max(x2,x3)));
int maxy = max(0,max(y1, max(y2,y3)));
int w = maxx - minx;
int h = maxy - miny;
//
Create a bitmap to hold the result
HBITMAP
hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);
HBITMAP
hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
HBITMAP
hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );
//
Draw the background color before we change mapping mode
HBRUSH
hbrBack = CreateSolidBrush( clrBack );
HBRUSH
hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );
destDC.PatBlt(
0, 0, w, h, PATCOPY );
::DeleteObject(
::SelectObject( destDC.m_hDC, hbrOld ) );
//
We will use world transform to rotate the bitmap
SetGraphicsMode(destDC.m_hDC,
GM_ADVANCED);
XFORM
xform;
xform.eM11
= cosine;
xform.eM12
= -sine;
xform.eM21
= sine;
xform.eM22
= cosine;
xform.eDx
= (float)-minx;
xform.eDy
= (float)-miny;
SetWorldTransform(
destDC.m_hDC, &xform );