Windows SDK GDI: How do I display a bitmap rotated?
Q: How do I display a bitmap rotated?
A: Mirroring or inverting a bitmap about its center point is not difficult to achieve since they do not change its rectangular shape with horizontal and vertical sides. Rotating the bitmap in multiples of 90 degrees also is easy. But displaying a bitmap rotated by an arbitrary angle is difficult since the shape is changed and screen resolutions come into picture. Here I have attempted to achieve this, using simple graphic transformation.
I am presenting a function 'MBRotate()', which takes, as inputs, the handle to a display device context, X and Y positions of the top left corner where the bitmap is to be displayed, handle to the bitmap object and angle of rotation in radians. Rotation direction is positive in anti-clockwise direction and point about which the rotation is performed is the insertion point of the bitmap, which is the top left corner of the bitmap. Since I am using trigonometric functions like 'sin()' and 'cos()' functions for coordinate transformations, I have included '' header file. I have written two simple functions to return rotated coordinates of a point and an array of points, latter using the former function. Steps to display the bitmap rotated involves two main steps:
Get the bitmap dimensions and compute the span when it is rotated. Get minimum and maximum of X and Y coordinates.
Scans all the points from between the minimum and maximum values of X and Y. Checks whether the point corresponds to a point within the bitmap dimensions. If it is, pick up the corresponding pixel of the bitmap and display.
The code is amply commented.
Code:
#include <math.h>
POINT Rotate(POINT pt, double theta)
{
// Gets horizontal and vertical resolution of display
HDC hDC = GetDC(NULL);
int XScale = GetDeviceCaps(hDC, LOGPIXELSX);
int YScale = GetDeviceCaps(hDC, LOGPIXELSY);
ReleaseDC(NULL, hDC);
// Transforms coordinates
int X = (int) (pt.x * cos(theta) - XScale * pt.y * sin(theta) / YScale);
int Y = (int) (pt.y * cos(theta) + YScale * pt.x * sin(theta) / XScale);
// Returns transformed point
POINT P = {X, Y};
return P;
}
LPPOINT Rotate(LPPOINT lppt, int size, double theta)
{
// Replaces the array with transformed points
for (int i = 0; i < size; i++)
lppt[i] = Rotate(lppt[i], theta);
// Returns the transformed array
return lppt;
}
BOOL BMRotate(HDC hdc, int x, int y, HBITMAP hbm, double theta = 0)
{
// Return value; TRUE if funtion successfully completed
BOOL Done = FALSE;
// Does nothing if bitmap handle is not valid
if (hbm == NULL)
return Done;
// Set mapping mode to MM_TEXT
int MapMode = SetMapMode(hdc, MM_TEXT);
// Creates a compatible memory DC handle to load bitmap
HDC hDC = CreateCompatibleDC(hdc);
if (hDC) // Device contect handle is not NULL
{
// Selects bitmap to device context
HBITMAP hBM = (HBITMAP) SelectObject(hDC, hbm);
// Gets bitmap dimensions
BITMAP BM;
GetObject(hbm, sizeof(BITMAP), &BM);
int W = BM.bmWidth, H = BM.bmHeight;
// Gets vertical & horizontal screen resolutions
int XScale = GetDeviceCaps(hdc, LOGPIXELSX);
int YScale = GetDeviceCaps(hdc, LOGPIXELSY);
// Evaluate corner points
POINT P[4];
P[0].x = 0, P[0].y = 0;
P[1].x = W, P[1].y = 0;
P[2].x = W, P[2].y = H;
P[3].x = 0, P[3].y = H;
// Rotates the rectangle
Rotate(P, 4, -theta);
// Computes span of rotated rectangle
for (int i = 1, MinX = P[0].x, MinY = P[0].y, MaxX = P[0].x, MaxY = P[0].y; i < 4; i++)
{
MinX = MinX < P[i].x ? MinX : P[i].x;
MaxX = MaxX > P[i].x ? MaxX : P[i].x;
MinY = MinY < P[i].y ? MinY : P[i].y;
MaxY = MaxY > P[i].y ? MaxY : P[i].y;
}
// Computes original rectangle
RECT R = {0, 0, W, H};
// Spans all points of rotated rectangle
for (int iX = MinX; iX < MaxX; iX++)
for (int iY = MinY; iY < MaxY; iY++)
{
POINT P = {iX, iY};
// Rotates back and checks whether in bitmap rectangle
P = Rotate(P, theta);
if (PtInRect(&R, P)) // Valid point
{
// Displays the bit in display device context
SetPixel(hdc, x + iX, y + iY, GetPixel(hDC, P.x, P.y));
}
}
// Deselects bitmap and deletes device context
SelectObject(hDC, hBM);
DeleteDC(hDC);
// Returns TRUE since operation is successful
Done = TRUE;
}
}
// Resets mapping mode to its original value
SetMapMode(hdc, MapMode);
// Returns FALSE since operation is failed
return Done;
}
Here is what I got when I rotated the bitmap by 30 degrees. Point about which rotation is performed is marked for illustration.
For bigger bitmaps, 'GetPixel()'/'SetPixel()' calls will be time consuming. This can be changed by getting the pixel information of the bitmap in an array and reconstructing the rotated pixels from this information. That is beyond the scope of this post since this attempts to illustrate the method to display rotated bitmaps.
Last edited by Andreas Masur; November 4th, 2006 at 12:54 PM.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.