Creating Special-Effect Bitmaps

Skipping is one way to achieve great bitmap effects

Saurabh Dixit

Saurabh is a contract programmer. He can be contacted at 12703 Jones Rd., Apt. 1014, Houston, TX 77070.


Recently, I needed special-effects bitmaps for a Windows project my group was working on. While commercial software is available for creating special effects, none filled our specific needs. Consequently, I was "appointed" to develop a set of custom special effects. In this article, I'll present details for a set of procedures I call "skipping" algorithms, which are the basis for the special-effect bitmap routines in Table 1.

Most effects can be achieved by combining several rectangular "blits" (block-bit transfers) from the bitmapped image and displaying them on the screen. The underlying logic that decides the order in which these block transfers take place defines the effect. The special-effects routines I present here assume that your GUI API has capabilities similar to those provided by the Windows BitBlt and associated graphics device interface (GDI) functions. (For information on BitBlt, see the accompanying text box entitled, "The BitBlt Function.")

There are several different types of special effects:

Most effects are referred to as "continuous," because part of the bitmap is gradually rendered onto the screen continuously from or toward an edge. When "skipping," you delay rendering an area of the bitmap. Skipping achieves great bitmap effects. Rather than slowly building the bitmap from scratch, as is the case with most effects, skipping appears to slowly merge the bitmap with the background. This is because the background remains visible for a longer period of the time it takes to render the bitmap completely.

The horizontal-skipping algorithm is a simple modification of the horizontal-curtain algorithm. Instead of traversing the bitmap continuously, alternate columns are skipped, leaving them periodically unchanged. Blitting does not stop when the columns meet at the center--it continues to the edge opposite the one it started from. Thus, columns missed on the pass from one edge are filled on the pass from the other.

Figure 1(a) shows the order in which the bitmap is blitted on screen to achieve this effect. The numbers in the vertical columns indicate the exact order in which the bitmap is realized. Note that when the column width is an even multiple of the bitmap width, the second pass begins at the last column. On the other hand, if the number of resulting columns is odd, then the second pass begins one column before the last. Reducing the column width to 1 results in a "line" of the bitmap. A column width larger than the bitmap width proves to be self-defeating.

Once you understand how horizontal skipping works, implementing a vertical-skipping algorithm is straightforward: Instead of skipping columns, you skip rows, applying similar considerations; see Figure 1(b).

You can also implement a skipping algorithm along the diagonal, which would generate the bitmap on screen in diagonal lines moving toward each other from the opposite ends of the diagonal of choice. This can be painstaking, as the diagonal line itself has to be generated using multiple block (square or rectangular) blits. The order in which you would blit the various bitmap blocks is given in Figure 2(a).

The principle behind the diagonal skipping algorithm remains the same as that for the horizontal- and vertical-skipping algorithms. The block size can be as small as 1'1 for a smooth diagonal line. Figure 2(b) shows how the bitmap has to be rendered to diagonal skipping.

How about skipping other shapes? A hollow-rectangle skipping algorithm can be achieved by blitting "hollow" rectangles from the bitmap onto the screen. The edges of the rectangles are portions of the bitmap. You blit hollow rectangles alternatively, moving simultaneously from the bitmap borders inward and from the bitmap center outward; see Figure 3. Both inward and outward passes leave rectangular portions of the background unchanged, to be "filled" by outward and inward passes moving in the opposite direction. Note that each rectangular blit is achieved through four BitBlt()s, one for each edge of the rectangle. In my experience, the effect looks best if edge thickness is fixed at 1.

Implementation

Listings One through Five provide a Windows implementation of the algorithms presented in this article, together with a demo program to view these effects. Listing One is the C source for the bitmap special-effects algorithms. I used the Borland C/C++ compiler in my efforts, although the implementation should be compiler independent. Besides the normally required arguments (source and destination device contexts, bitmap dimensions), the function FXSkipHorizontal() takes the width of each vertical column that the effect will use. An additional delay parameter is also passed to slow or accelerate the speed of the effect. The implementation takes care of whether the requested column's width is a factor of the bitmap width, and works as long as the column size is not specified to be 0. Similarly, FXSkipVertical() takes the height of each horizontal row and the delay. The FXSkipDiagonalSquares() and FXSkipDiagonals() implementations assume that the bitmap width and height are multiples of the element size. Finally, FXSkipRectangles() does not have room for a user-specified edge thickness for the rectangles.

Listing Two is the demo program. Listings Three through Five make up the remaining files in the project. The absence of a make file listing stems from my use of the Borland C++ IDE, which works with Project (.PRJ) files rather than having to work with (.MAK) files. I used the small model for my executable.

The demo program uses two bitmaps, "bitmap1" and "bitmap2," which reside in the executable. When you compile the sources, make sure to qualify two bitmaps with these names in your resource (.RC) file. Double clicking the left mouse button in the application client-window area cycles through the different effects. Double clicking the right mouse button alternates the bitmaps. The RC files, executables, and additional files are available electronically; see "Availability," page 3.

The bitmaps accompanying the source code for this article are in the standard Windows .BMP format and use 16 colors. If you are going to attempt any kind of scaling before you use the effects, or plan to use 256, 64K, or more color bitmaps, then you will have to work within the capabilities of your GUI library and to some extent, your video hardware.

To use bitmaps with colors not found in the standard system palette, you will first have to extract the palette information from the bitmap and select it into the appropriate device contexts using SelectPalette() and RealizePalette(). For stretching in memory contexts, remember to use SetStretchBltMode() with the STRETCH_DELETESCANS parameter, if you wish to preserve the colors on your bitmap.

I compiled and executed the source on a 486, 66-MHz PC with 8 Mbytes of RAM. The delay (in milliseconds) used by the routines is interpreted in terms of CPU clock cycles. The actual speed of your effect will therefore depend on the frequency of clock ticks on your target machine.

Conclusion

I have used BitBlt() in all my implementations except FXSkipDiagonals(), where I had to use regions along with clipping to get the true diagonal-skipping effect. However, region operations are often slow, especially as the regions grow in complexity. Also, regions use up quite a bit of memory.

My implementation deals only with the BitBlt() SRCCOPY raster operation. You might want to experiment with other values for the rop parameter to BitBlt().

The code is yours to modify and use as you deem fit as long as you don't wipe out the headers at the beginning of each source file. If you have had similar experiences, I would appreciate hearing about them. Any effect is most useful when you have better control over the time it takes to prepare and render it. Please send me a copy of your efforts if you manage to make my algorithm implementations more efficient.

The BitBlt() Function

The BitBlt() function copies a bitmap from a specified-device context to a destination-device context. BitBlt() can display both monochrome and color bitmaps. No special steps are required to display bitmaps of different formats. However, BitBlt() can convert the bitmap if its color format is not the same as that of the destination device. For example, when displaying a color bitmap on a monochrome screen, BitBlt() converts the pixels having the current background color to white and all other pixels to black. The parameters to BitBlt() are shown in Example 1. rop specifies the raster operation to be performed. Raster-operation codes define how the graphics device interface (GDI) combines colors in output operations that involve a current brush, a possible source bitmap, and a destination bitmap. rop parameters are listed in Table 2. The return value is nonzero if the function is successful; otherwise, it is zero.

----S.D.

Example 1 BitBlt() and its parameters.

Table 1 Skipping-algorithm implementations.

Table 2 The BitBlt function's rop parameter options.

Figure 1 (a) Skipping columns; (b) skipping rows.

Figure 2 (a) Skipping squares; (b) skipping diagonal lines.

Figure 3 Hollow-rectangle skipping.

Listing One


//      File: FX.c
//      Author: Saurabh Dixit
//      Purpose: Implementations for various bitmap effects algorithms

#include <windowsx.h>
#include <math.h>
#include <time.h>

BOOL FXSkipHorizontal (HDC hdc, // screen device context
                 HDC hdcMem, // memory device context
                 int bmpWidth, // bitmap width
                 int bmpHeight, // bitmap height
                 int colwidth, // skip width
                 int delay // delay in milliseconds
                )
{
   int l, r, m, d;
   int left, right;
   DWORD nexttime;
   BOOL success;

   // initialize
   m = bmpWidth % colwidth;
   d = bmpWidth / colwidth;
   if (m) { // bitmap width not an exact multiple of column width
      if (d % 2) // odd number of elements
      r = d * colwidth;
      else // even number of elements
      r = (d - 1) * colwidth;
   }
   else { // bitmap width is an exact multiple of column width
      if (d % 2) // odd number of elements
      r = bmpWidth - (colwidth << 1);
      else // even number of elements
      r = bmpWidth - colwidth;
   }
   l = 0;

   left = l;
   right = bmpWidth;


   nexttime = GetTickCount () + delay;

   while (r > left || l < right) {
      // blit from the left
      success = BitBlt (hdc, l, 0, colwidth, bmpHeight,
               hdcMem, l, 0,
               SRCCOPY);
      if (!success)
      break;
      // blit from the right
      success = BitBlt (hdc, r, 0, colwidth, bmpHeight,
               hdcMem, r, 0,
               SRCCOPY);
      if (!success)
      break;
      l += (colwidth << 1); // skip a column to the right
      r -= (colwidth << 1); // skip a column to the left

      // wait a little
      while (GetTickCount () < nexttime);
      nexttime += delay;
   }

   return success;
}

BOOL FXSkipVertical (HDC hdc, // screen device context
               HDC hdcMem, // memory device context
               int bmpWidth, // bitmap width
               int bmpHeight, // bitmap height
               int rowheight, // skip height
               int delay // delay in milliseconds
              )
{
   int t, b, m, d;
   int top, bottom;
   DWORD nexttime;
   BOOL success;

   // initialize
   m = bmpHeight % rowheight;
   d = bmpHeight / rowheight;
   if (m) { // bitmap height not an exact multiple of row height
      if (d % 2) // odd number of elements
      b = d * rowheight;
      else // even number of elements
      b = (d - 1) * rowheight;
   }
   else { // bitmap height is an exact multiple of row height
      if (d % 2) // odd number of elements
      b = bmpHeight - (rowheight << 1);
      else // even number of elements
      b = bmpHeight - rowheight;

   }
   t = 0;

   top = t;
   bottom = bmpHeight;

   nexttime = GetTickCount () + delay;

   while (b > top || t < bottom) {
      // blit from the top
      success = BitBlt (hdc, 0, t, bmpWidth, rowheight,
               hdcMem, 0, t,
               SRCCOPY);
      if (!success)
      break;
      // blit from the bottom
      success = BitBlt (hdc, 0, b, bmpWidth, rowheight,
               hdcMem, 0, b,
               SRCCOPY);
      if (!success)
      break;
      t += (rowheight << 1); // skip a row down
      b -= (rowheight << 1); // skip a row up

      // wait a sec!...
      while (GetTickCount () < nexttime);
      nexttime += delay;
   }

   return success;
}

BOOL FXSkipDiagonalSquares (HDC hdc, // screen device context
                   HDC hdcMem, // memory device context
                   int bmpWidth, // bitmap width
                   int bmpHeight, // bitmap height
                   int size, // square element size
                   int delay // delay in milliseconds
               )
{
   int id, l, t, nlines;
   DWORD nexttime;
   BOOL success;

   // formula for calculating number of diagonal 'lines' in which
   // (size X size) squares are blitted
   nlines =  (bmpWidth + bmpHeight) / size - 1;

   nexttime = GetTickCount () + delay;

   for (id = 1; id <= nlines; id ++) {
      if (id % 2) { // odd line id
      l = id * size;

      if (l > bmpWidth)
         l = bmpWidth;
      t = (id - l / size + 1) * size;

      // blit all squares in line
      while (t <= bmpHeight) {
         success = BitBlt (hdc, l - size, t - size, size, size,
                     hdcMem, l - size, t - size,
                     SRCCOPY);
         if (!success)
            break;
         l -= size;
         t += size;
      }
      if (!success)
         break;
      }
      else { // even line id
      // check here for starting position of second pass
      if ((bmpWidth + bmpHeight) / size % 2)
         l = bmpWidth + (2 - id) * size;
      else
         l = bmpWidth + (1 - id) * size;
      t = bmpHeight;

      // blit all squares in line
      while (l <= bmpWidth) {
         success = BitBlt (hdc, l - size, t - size, size, size,
                     hdcMem, l - size, t - size,
                     SRCCOPY);
         if (!success)
            break;
         l += size;
         t -= size;
      }
      if (!success)
         break;
      }

      // wait a sec!...
      while (GetTickCount () < nexttime);
      nexttime += delay;
   }

   return success;
}

BOOL FXSkipDiagonals (HDC hdc, // screen device context
                HDC hdcMem, // memory device context
                int bmpWidth, // bitmap width
                int bmpHeight, // bitmap height
                int thickness, // 'drawing brush' thickness
                int delay // delay in milliseconds

               )
{
   int id, l, t, ndiags;
   POINT p[4];
   DWORD nexttime;
   BOOL success;
   HRGN hrgn;

   // formula for calculating the number of diagonal portions
   ndiags = (bmpWidth + bmpHeight) / thickness;

   nexttime = GetTickCount () + delay;

   for (id = 1; id <= ndiags; id ++) {
      switch (id) {
      case 1: // always a triangle
         p[0].x = p[0].y = 0;
         p[1].x = thickness; p[1].y = 0;
         p[2].x = 0; p[2].y = thickness;
         hrgn = CreatePolygonRgn (p, 3, ALTERNATE);
         break;
      case 2:
         if (!(ndiags % 2)) { // a triangle
            p[0].x = bmpWidth; p[0].y = bmpHeight;
            p[1].x = bmpWidth - thickness; p[1].y = bmpHeight;
            p[2].x = bmpWidth; p[2].y = bmpHeight - thickness;
            hrgn = CreatePolygonRgn (p, 3, ALTERNATE);
            break;
         }
         // else - not a triangle so fall thru
      default:
         if (id % 2) {
            p[0].x = (id - 1) * thickness;
            p[1].x = p[0].x + thickness;
            if (p[0].x > bmpWidth) {
            p[0].y = p[0].x - bmpWidth;
            p[0].x = bmpWidth;
            }
            else
            p[0].y = 0;
            if (p[1].x > bmpWidth) {
            p[1].y = p[1].x - bmpWidth;
            p[1].x = bmpWidth;
            }
            else
            p[1].y = 0;

            p[2].y = id * thickness;
            p[3].y = p[2].y - thickness;
            if (p[2].y > bmpHeight) {
            p[2].x = p[2].y - bmpHeight;
            p[2].y = bmpHeight;

            }
            else
            p[2].x = 0;
            if (p[3].y > bmpHeight) {
            p[3].x = p[3].y - bmpHeight;
            p[3].y = bmpHeight;
            }
            else
            p[3].x = 0;
         }
         else {
            if (ndiags % 2)
            p[0].x = bmpWidth - (id - 1) * thickness;
            else
            p[0].x = bmpWidth - (id - 2) * thickness;
            p[1].x = p[0].x - thickness;
            if (p[0].x < 0) {
            p[0].y = bmpHeight + p[0].x;
            p[0].x = 0;
            }
            else
            p[0].y = bmpHeight;
            if (p[1].x < 0) {
            p[1].y = bmpHeight + p[1].x;
            p[1].x = 0;
            }
            else
            p[1].y = bmpHeight;

            if (ndiags % 2)
            p[2].y = bmpHeight - id * thickness;
            else
            p[2].y = bmpHeight - (id - 1) * thickness;
            p[3].y = p[2].y + thickness;
            if (p[2].y < 0) {
            p[2].x =  bmpWidth + p[2].y;
            p[2].y = 0;
            }
            else
            p[2].x = bmpWidth;
            if (p[3].y < 0) {
            p[3].x = bmpWidth + p[3].y;
            p[3].y = 0;
            }
            else
            p[3].x = bmpWidth;
         }
         hrgn = CreatePolygonRgn (p, 4, ALTERNATE);
         break;
      }
      // Select appropriate clipping region in device context
      // for this line and blit
      if (hrgn) {

      SelectClipRgn (hdc, hrgn);
      success = BitBlt (hdc, 0, 0, bmpWidth, bmpHeight,
                  hdcMem, 0, 0,
                  SRCCOPY);
      SelectClipRgn (hdc, 0);
      DeleteRgn (hrgn);
      }
      else
      success = FALSE;
      if (!success)
      break;

      // wait a little
      while (GetTickCount () < nexttime);
      nexttime += delay;
   }

   return success;
}

BOOL FXSkipRectangles (HDC hdc, // screen device context
                 HDC hdcMem, // memory device context
                 int bmpWidth, // bitmap width
                 int bmpHeight, // bitmap height
                 int delay // delay in milliseconds
                )
{
   int xl, yl, halfsize, minsize, xr, yr, temp;
   int inwidth, inheight, outwidth, outheight;
   BOOL stilldrawing, success;
   DWORD nexttime;

   // initialize
   xl = 0;
   yl = 0;
   minsize = (bmpWidth > bmpHeight ? bmpHeight : bmpWidth);
   xr = minsize % 2 ? (minsize >> 1) : (minsize >> 1) + 1;
   temp = xl;
   while (temp <= xr)
      temp += 2;
   if (temp == xr + 2)
      xr ++;

   yr = xr;

   // store approximate center for terminating inward pass
   halfsize = xr;

   nexttime = GetTickCount () + delay;

   while (xl <= halfsize || yl <= halfsize) {
      stilldrawing = FALSE;

      // draw the rectangle going inward
      if (xl <= halfsize && yl <= halfsize) {

      inwidth = bmpWidth - (xl << 1);
      inheight = bmpHeight - (yl << 1);

      // top edge
      success = BitBlt (hdc, xl, yl, inwidth, 1,
                  hdcMem, xl, yl,
                  SRCCOPY);
      if (!success)
         break;

      // left edge
      success = BitBlt (hdc, xl, yl, 1, inheight,
                  hdcMem, xl, yl,
                  SRCCOPY);
      if (!success)
         break;

      // bottom edge
      success = BitBlt (hdc, xl, yl + inheight, inwidth + 1, 1,
                  hdcMem, xl, yl + inheight,
                  SRCCOPY);
      if (!success)
         break;

      // right edge
      success = BitBlt (hdc, xl + inwidth, yl, 1, inheight + 1,
                  hdcMem, xl + inwidth, yl,
                  SRCCOPY);
      if (!success)
         break;
      stilldrawing = TRUE;
      }

      // draw the rectangle going outward
      if (xr >= 0 && yr >= 0) {
      outwidth = bmpWidth - (xr << 1);
      outheight = bmpHeight - (yr << 1);

      // top edge
      success = BitBlt (hdc, xr, yr, outwidth, 1,
                  hdcMem, xr, yr,
                  SRCCOPY);
      if (!success)
         break;

      // left edge
      success = BitBlt (hdc, xr, yr, 1, outheight,
                  hdcMem, xr, yr,
                  SRCCOPY);
      if (!success)
         break;

      // bottom edge

      success = BitBlt (hdc, xr, yr + outheight, outwidth + 1, 1,
                  hdcMem, xr, yr + outheight,
                  SRCCOPY);
      if (!success)
         break;

      // right edge
      success = BitBlt (hdc, xr + outwidth, yr, 1, outheight + 1,
                  hdcMem, xr + outwidth, yr,
                  SRCCOPY);
      if (!success)
         break;
      stilldrawing = TRUE;
      }
      if (stilldrawing) {
      // skip a rectangle moving inward
      xl += 2; yl += 2;

      // skip a rectangle moving outward
      xr -= 2; yr -= 2;
      }
      else
      break;

      // wait a little
      while (GetTickCount () < nexttime);
      nexttime += delay;
   }

   return success;
}


Listing Two x


//      File: FXDemo.c
//      Author: Saurabh Dixit
//      Purpose: Demonstrating Bitmap Special Effects

#include <windowsx.h>
#include "fx.h"

#define WIDTH       640
#define HEIGHT      480

#define DELAY       25
#define COLWIDTH         4
#define   ROWHEIGHT 2
#define   SIZESQUARE     5
#define   SIZEWIDTH      10

#define FX_SKIPHORIZONTAL          1

#define FX_SKIPVERTICAL       2
#define   FX_SKIPDIAGONALSQUARES   3
#define   FX_SKIPDIAGONALS         4
#define FX_SKIPRECTANGLES          5

LRESULT CALLBACK WndProc (HWND, UINT, UINT, LONG);
BOOL RenderEffect (HDC, HBITMAP, int);

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
              LPSTR lpszCmdParam, int nCmdShow)
{
   char     ClassName[] = "WHO-CARES";
   char     AppName[] = "FXDemo by Saurabh Dixit";
   HWND     hwnd;
   MSG      msg;
   WNDCLASS wc;

   if (hPrevInstance) {
      MessageBox (NULL,
            "Only one instance allowed!",
            AppName,
            MB_ICONINFORMATION | MB_TASKMODAL);
      MessageBeep (MB_ICONINFORMATION);
      return FALSE;
   }

   wc.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
   wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
   wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = ClassName;

   RegisterClass (&wc) ;

   hwnd = CreateWindow (ClassName,
               AppName,
               WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
               WS_MINIMIZEBOX | WS_BORDER,
               (GetSystemMetrics (SM_CXSCREEN) - WIDTH) >> 1,
               (GetSystemMetrics (SM_CYSCREEN) - HEIGHT) >> 1,
               WIDTH,
               HEIGHT,
               NULL,
               NULL,
               hInstance,
               NULL);

   ShowWindow (hwnd, nCmdShow);

   UpdateWindow (hwnd);

   while (GetMessage (&msg, NULL, 0, 0)) {
      TranslateMessage (&msg);
      DispatchMessage (&msg);
   }
   return msg.wParam;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                 WPARAM wParam, LPARAM lParam)
{
   PAINTSTRUCT ps;
   LRESULT     ret;
   HBITMAP     hbitmap;
   HCURSOR     hcursor;
   HINSTANCE   hinst;

   static char bmpname[8] = {'b', 'i', 't', 'm', 'a', 'p', '1', 0};
   static int  effect = FX_SKIPHORIZONTAL;

   switch (message) {
      case WM_NCHITTEST:
      ret = DefWindowProc (hwnd, message, wParam, lParam);
      if (ret == HTCAPTION)
         // so window can't be moved, just for kicks
         ret = HTCLIENT;
      return ret;

      case WM_PAINT:
      BeginPaint (hwnd, &ps) ;
      hinst = GetWindowInstance (hwnd);
      hbitmap = LoadBitmap (hinst, bmpname);
      if (hbitmap) {
         if (!RenderEffect (ps.hdc, hbitmap, effect))
            MessageBeep (MB_ICONSTOP);
         DeleteBitmap (hbitmap);
      }
      else
         MessageBeep (MB_ICONSTOP);
      EndPaint (hwnd, &ps) ;
      return FALSE;

      case WM_RBUTTONDBLCLK:
      // switch the bitmaps
      switch (bmpname[6]) {
         case '1': bmpname[6] = '2'; break;
         case '2': bmpname[6] = '1'; break;
      }
         // don't erase the background
      InvalidateRect (hwnd, NULL, FALSE);
      UpdateWindow (hwnd);
      return FALSE;


      case WM_LBUTTONDBLCLK:
      // cycle through the effects
      effect ++;
      if (effect > FX_SKIPRECTANGLES)
         effect = FX_SKIPHORIZONTAL;
      // erase the background
      InvalidateRect (hwnd, NULL, TRUE);
      UpdateWindow (hwnd);
      return FALSE;


      case WM_DESTROY:
      PostQuitMessage (0);
      return FALSE;
   }

   return DefWindowProc (hwnd, message, wParam, lParam);
}

BOOL RenderEffect (HDC hdc, HBITMAP hbitmap, int effect)
{
   BITMAP bm;
   HDC hdcMem;
   DWORD dwSize;

   BOOL success;

   hdcMem = CreateCompatibleDC (hdc);
   SelectBitmap (hdcMem, hbitmap);
   SetMapMode (hdcMem, GetMapMode (hdc));

   GetObject (hbitmap, sizeof (BITMAP), (LPSTR) &bm);

   switch (effect) {
      case FX_SKIPHORIZONTAL:
      success = FXSkipHorizontal (hdc, hdcMem,
                         bm.bmWidth, bm.bmHeight,
                         COLWIDTH, DELAY);
      break;
      case FX_SKIPVERTICAL:
      success = FXSkipVertical (hdc, hdcMem,
                       bm.bmWidth, bm.bmHeight,
                       ROWHEIGHT, DELAY);
      break;
      case FX_SKIPDIAGONALSQUARES:
      success = FXSkipDiagonalSquares (hdc, hdcMem,
                           bm.bmWidth, bm.bmHeight,
                           SIZESQUARE, DELAY);
      break;
      case FX_SKIPDIAGONALS:
      success = FXSkipDiagonals (hdc, hdcMem,
                        bm.bmWidth, bm.bmHeight,
                        SIZEWIDTH, DELAY);
      break;
      case FX_SKIPRECTANGLES:

      success = FXSkipRectangles (hdc, hdcMem,
                         bm.bmWidth, bm.bmHeight,
                         DELAY);
      break;
      default: // just in case
      success = BitBlt (hdc, 0, 0, bm.bmWidth, bm.bmHeight,
                  hdcMem, 0, 0,
                  SRCCOPY);
   }

   DeleteDC (hdcMem);
   return success;
}




Listing Three


//      File: FX.h
//      Author: Saurabh Dixit
//      Purpose: Prototypes for Bitmap Effects

BOOL FXSkipHorizontal (HDC, HDC, int, int, int, int);
BOOL FXSkipVertical (HDC, HDC, int, int, int, int);
BOOL FXSkipDiagonalSquares (HDC, HDC, int, int, int, int);
BOOL FXSkipDiagonals (HDC, HDC, int, int, int, int);
BOOL FXSkipRectangles (HDC, HDC, int, int, int);


Listing Four


;--------------------------------------------------------------------------
; File:             FXdemo.def
; Author:           Saurabh Dixit
; Purpose:          Module definition file
;--------------------------------------------------------------------------

NAME           FXDEMO

DESCRIPTION    'Special Bitmap Effects Demo'
EXETYPE        WINDOWS
STUB           'WINSTUB.EXE'
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD MOVEABLE MULTIPLE
HEAPSIZE       8192
STACKSIZE      8192


Listing Five


//      File: FXDemo.rc
//      Author: Saurabh Dixit
//      Purpose: Resource file for including bitmaps

bitmap1 BITMAP class.bmp
bitmap2 BITMAP tiger.bmp


Copyright © 1995, Dr. Dobb's Journal