Wednesday, March 22, 2006

CodeGuru: Rotate a Bitmap at Any Angle Without GetPixel/SetPixel:

Using GetDIBits

This code is much faster because it uses GetDIBits to get the 32-bit representation of the bitmap to rotate. All operations are done in local memory rather than in slow API calls such as GetPixel or even BitBlt. I use the 32-bit representation mainly for its ease of use. When working with other color depths, you have to add some padding bytes at the end of each scan line, which is not necessary in 32 bits. Moreover, it allows easier access to the memory.

pBGR MyGetDibBits(HDC hdcSrc, HBITMAP hBmpSrc, int nx, int ny)
{
BITMAPINFO bi;
BOOL bRes;
pBGR buf;

bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = nx;
bi.bmiHeader.biHeight = - ny;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = nx * 4 * ny;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;

buf = (pBGR) malloc(nx * 4 * ny);
bRes = GetDIBits(hdcSrc, hBmpSrc, 0, ny, buf, &bi,
DIB_RGB_COLORS);
if (!bRes) {
free(buf);
buf = 0;
}
return buf;
}

RotateMemoryDC

This function does all the work. Apart from getting the source coordinate from a target coordinate, it is very straightforward. The formulas I use are these:
orgX = (cA * (((float) stepX + OfX) + CtX * (cA - 1)) + sA * (((float) stepY + OfY) + CtY * (sA - 1))) / cA*cA + sA*sA;

orgY = CtY + (CtX - ((float) stepX + OfX)) * sA + cA *(((float) stepY + OfY) - CtY + (CtY - CtX) * sA);

cA is just cos(angle) and sA is sin(angle). CtX and CtY are the center of the source image and OfX and OfY are the offsets of the source image inside the target image. This is necessary because the target image has to be bigger than the source.

I actually make a point of using floating point operations in the inner loop to show that the performance does not depend on these. In the old days of DOS, when PCs were slow at floating point operations and graphics manipulation was fast because it consisted only of accessing a special part of the system memory, the bottleneck was indeed the floating point operations. With GDI, this is unfortunately not true anymore. Most of the time is taken up by getting the bitmaps and setting them.

Changing the calculations to fixed point math will only result in a marginal improvement in the overall speed.
General Notes

The program is based on the sample "Hello World" application generated by Visual C++ 6. I just added the new drawing code and a Timer so that the bitmap rotates continuously.

0 Comments:

Post a Comment

<< Home