HOWTO: Display a Bitmap into a Rotated or Non-rectangular Area

ID: Q186589

The information in this article applies to:

SUMMARY

While the Windows GDI provides numerous functions that allow developers to display bitmaps into rectangular areas, it doesn't provide a way to display into areas defined by an arbitrary set of vertices. The sample code in this article demonstrates a simple way to map a bitmap from a rectangular area into an area defined by four vertices. You can use this technique to rotate, invert, stretch, or twist a bitmap.

NOTE: For simplicity, the code is written to use GetPixel() and SetPixel() to retrieve and set pixel values in the referenced display contexts. You can achieve much greater performance by rewriting the code to use DIB sections and directly manipulating the bits of the surface.

MORE INFORMATION

Sample Code

   #define SHIFTS 15            // Extend ints to limit round-off error.
   #define THRESH (1 << SHIFTS) // Threshold for pixel size value.

   /* CopySourceToDest()
    *
    * Re-map a rectangular area into an area defined by four vertices.
    *
    * This function does all the real re-mapping work by recursively
    * dividing both the source and destination areas until the
    * area specified by the destination is a single pixel. At this
    * point, it copies the pixel from the source to the destination.
    *
    * By quartering both the source and the destination areas, the
    * relationship between the area in the source and the area in the
    * destination is preserved.
    *
    * NOTES:
    *   This function would be much faster if it were implemented to
    *   directly manipulate the surface bits of a DIB section rather than
    *   using GetPixel() / SetPixel().
    */
   void CopySourceToDest(HDC hdcDst,  POINT ul, POINT ur, POINT lr,
                         POINT ll, HDC hdcSrc,  LONG  x1, LONG  y1,
                         LONG  x2, LONG  y2)
   {
       POINT tm,lm,rm,bm,m;
       LONG mx,my;

       // Does the destination area specify a single pixel?
       if ((abs(ul.x - ur.x) < THRESH) &&
           (abs(ul.x - lr.x) < THRESH) &&
           (abs(ul.x - ll.x) < THRESH) &&
           (abs(ul.y - ur.y) < THRESH) &&
           (abs(ul.y - lr.y) < THRESH) &&
           (abs(ul.y - ll.y) < THRESH))
       {   // Yes.
           COLORREF cr;

        cr = GetPixel(hdcSrc, (x1 >> SHIFTS), (y1 >> SHIFTS));
           SetPixel(hdcDst, (ul.x >> SHIFTS), (ul.y >> SHIFTS), cr);
       } else {  // No
           // Quarter the source and the destination, and then recurse.
           tm.x = (ul.x + ur.x) >> 1;
           tm.y = (ul.y + ur.y) >> 1;
           bm.x = (ll.x + lr.x) >> 1;
           bm.y = (ll.y + lr.y) >> 1;

           lm.x = (ul.x + ll.x) >> 1;
           lm.y = (ul.y + ll.y) >> 1;
           rm.x = (ur.x + lr.x) >> 1;
           rm.y = (ur.y + lr.y) >> 1;

           m.x = (tm.x + bm.x) >> 1;
           m.y = (tm.y + bm.y) >> 1;

           mx = (x1 + x2) >> 1;
           my = (y1 + y2) >> 1;

           CopySourceToDest(hdcDst, ul, tm, m, lm, hdcSrc, x1, y1,
                            mx, my);
           CopySourceToDest(hdcDst, tm, ur, rm, m,  hdcSrc, mx, y1,
                            x2, my);
           CopySourceToDest(hdcDst, m,  rm, lr, bm, hdcSrc, mx, my,
                            x2, y2);
           CopySourceToDest(hdcDst, lm, m,  bm, ll, hdcSrc, x1, my,
                            mx, y2);
       };

   };

   /* WarpBlt()
    *
    * Re-map a rectangular area into an area defined by four vertices .
    *
    */
   void WarpBlt(HDC hdcDst,  POINT ul, POINT ur, POINT lr, POINT ll,
                HDC hdcSrc,  LONG  x1, LONG  y1, LONG  x2, LONG  y2)
   {
       // Shift all values to help reduce round-off error.
       ul.x <<= SHIFTS;
       ul.y <<= SHIFTS;
       ur.x <<= SHIFTS;
       ur.y <<= SHIFTS;

       lr.x <<= SHIFTS;
       lr.y <<= SHIFTS;
       ll.x <<= SHIFTS;
       ll.y <<= SHIFTS;

       x1 <<= SHIFTS;
       y1 <<= SHIFTS;
       x2 <<= SHIFTS;
       y2 <<= SHIFTS;

       CopySourceToDest(hdcDst, ul, ur, lr, ll,
                        hdcSrc, x1, y1, x2, y2);
   }

   /* DemonstrateWarpBlt()
    *
    * Re-map the image of the entire screen into an area
    * defined by four random vertices .
    *
    */
   void DemonstrateWarpBlt(HWND hWnd)
   {
       HDC hdcScreen = GetDC(NULL);   // Source DC.
       HDC hdcWindow = GetDC(hWnd);   // Destination DC.
       POINT ul,ur,lr,ll;             // Vertices for the destination area.
       int iXRes, iYRes;              // Extents of the screen.
       RECT rc;                       // Extents of the window.

       // Get the size of the screen.
       iXRes = GetDeviceCaps(hdcScreen, HORZRES);
       iYRes = GetDeviceCaps(hdcScreen, VERTRES);

       // Get the size of our client area.
       GetClientRect(hWnd, &rc);

       // Define a random destination area that fits in the client area.
       ul.x = rand() % rc.right;
       ul.y = rand() % rc.bottom;
       ur.x = rand() % rc.right;
       ur.y = rand() % rc.bottom;
       lr.x = rand() % rc.right;
       lr.y = rand() % rc.bottom;
       ll.x = rand() % rc.right;
       ll.y = rand() % rc.bottom;

       // Copy the entire screen into the destination area.
       WarpBlt(hdcWindow, ul, ur, lr, ll,
               hdcScreen, 0, 0, iXRes, iYRes);
   }

Additional query words: kbDSupport kbdsd kbGDI kbBitmap kbDevContext kbDisplay
Keywords          : kbcode
Version           : WINNT:
Platform          : winnt
Issue type        : kbhowto

Last Reviewed: June 2, 1998