dcsimg

Rotate a Bitmap at Any Angle Without GetPixel/SetPixel

Environment: Windows GDI / Win32 API

Reason For This Article

There is already an article on CodeGuru about rotating a bitmap at any angle. Unfortunately, it is very slow and produces some artifacts when rotating. The comments have focused on optimizing the floating point operations, but that is not the issue. The real issue is that GetPixel and SetPixel are abysmally slow. So, I wrote this program to demonstrate that it's not the fault of the floating point calculations.

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.

Downloads

Download demo project - 25 Kb
Download source - 10 Kb


This article was originally published on November 19th, 2002

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date