Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Quick Approximations of Polygonal Areas Using BITBLT


SP91: QUICK APPROXIMATIONS OF POLYGONAL AREAS USING BITBLT

QUICK APPROXIMATIONS OF POLYGONAL AREAS USING BITBLT

This article contains the following executables: BITLIT.ASC

Nancy Nicolaisen

Nancy has worked for the U.S. Geological Survey and the National Ocean Service in Alaska. She is the author of a hypertext publishing system for maps and geographically related data.


Several years ago, Garrison Kiellor visited Alaska, putting on a July 4th show that included a side-splitting monologue about a couple visiting from the Midwest who used a roadmap to decide where to go sightseeing. The humor in this is a bit of an inside joke because on one hand, our entire state has fewer miles of road than a small county in the Lower 48; on the other hand, Alaska has one-half of the coastline and one-fifth the landmass of the entire U.S. If we are short on roads, we are even shorter on destinations.

As Gertrude Stein said, "When you get there there is no there there," unless you count our majestic wilderness, which, of course, counts very much to us. In the job of managing natural resources and doing environmental research, the biggest challenges we have involve the logistics of putting scientists in the field, getting back their data, analyzing the information, and communicating it to decision makers and operational users.

Over the years it has become clear that we need to put as much data as possible in the hands of the users in an approachable form. Natural scientists tend to use Geographic Information System (GIS) tools, and invariably, these tools mean a big computer and a data processing bureaucracy. For complex, exacting projects, this is a good arrangement. But for quick, general answers to uncomplicated questions, things become unsatisfactory for both users and DP professionals.

In short, what we need is a "backpack" GIS that can be carried into the field, the meeting room, or the press briefing. We need to do things such as display cartographically faithful maps and employ "hypermaps" to enrich the information content with pictures, charts, graphs, narrative text, and raw data. We need to interact with the data in simple ways. What follows is a look at how we might provide a quick, reasonably accurate answer to a simple question such as, "How much of this habitat zone was covered by the oil spill?"

In this article, I examine the use of graphics and Boolean raster operations to estimate areas. The strategy for doing this relies on determining the unit area represented by a single pixel, distinguishing which pixels belong to the visual representation of the area we want to estimate, counting them, and multiplying by our unit area. For example, Figure 1 and Figure 2 show irregularly shaped areas we can combine to find various areas. Figure 3 shows the pull-down menu listing the various combinations of areas we can do. Figure 4 shows the result of estimating the non-intersecting area of Areas 1 and 2.

Examining the Source Code

Let's start by looking at the C source in Listing Two, page 14. (The entire system, including header files, icons, and so on is available electronically; see "Availability" on page 3.) The program begins with the normal initialization tasks. For the first instance of the program, we need to register the window class and create any generic objects from which all instances could benefit. Notice that when we register this class, the rClass.cbWndExtra field is set to the size of a WORD, which is defined in Windows.h as being an unsigned short. This reserves space for us in the Window structure of each separate instance of the application. This word will hold the value of the constant representing the user's choice from the pull-down menu, and is used to switch the cases in the paint procedure.

Next we need to do the initialization required for every instance of the program. We assign the instance handle to a global variable and create the window based upon the previously registered class. We show the new window, using the cmdShow parameter provided to us by Windows.

At this point, we are waiting in the main program, WinMain, for the messages to come rolling in. WinMain contains the message dispatch loop. When they arrive, those messages will be delivered to the WindowProc. The WM_COMMAND message tells us that the user has spoken; naturally, we will want to do something about that as quickly as possible. The wParam (for Word Parameter) contains a constant that identifies the pull-down menu choice of the user. We will store this value in the space allocated for this purpose in the window structure and call the paint procedure.

Painting the Window

When we enter the paint procedure there are some routine things to do. First, we need to get a handle to a display context and find the dimension in pixels of the client area of our window. We want the size of a unit in the logical coordinate system to be the same in both X and Y directions, so we set the mapping mode to MM_ISOTROPIC (or to put it another way: (xUnitLength/yUnitLength = 1)). This is important, because we estimate areas of shapes by counting the number of pixels in the area and multiplying by the value fAreaPerPixel, which we define as the square area in the logical coordinate system represented by one pixel.

When we define the size of the viewport and the window, notice that the signs of the Y extents differ. This aligns the lower-left corner of our window on the display with the origin of our logical coordinate system. As a result of this alignment, the X axis will increase to the right and the Y axis will increase, going upward.

Next we create a memory display context with the same default properties as the DC we got by calling BeginPaint. It's worth stressing that while the default attributes of the memory DC are like those of our visual DC, the size, location, and properties of the drawing surface are not yet defined. The newly created DC has a monochrome "display surface" composed of one pixel. We must explicitly define its coordinate systems and mapping properties. The memory "display surface" itself comes into being when we create a bitmap and select it into the memory DC.

We create memory bitmaps to hold an image of Area 1 and Area 2, and play the previously recorded metafiles onto them to construct those images. The metafiles are used here for the sake of convenience. Notice the calls to SaveDC and RestoreDC that bracket the PlayMetaFile call. Metafiles can and frequently do affect the settings of a DC. It is a good practice to anticipate this and ensure that the state of the DC will be acceptable after the metafile has played.

At this point, there are only two real jobs left. First we need to combine the bitmaps, and then we need to count the bits that represent the area.

The Versatile BitBlt

A quick look at the source lines for the paint procedure in Listing Two will reveal that we use BitBlt to combine the bitmaps into a single image. BitBlt is one of the most lovable graphics tools around -- it's fast, flexible, and though this example is a little short on spectacular graphics, it can provide them. BitBlt takes several parameters: a handle to a destination DC; X and Y coordinates for the logical origin of the bitmap on the destination; logical width and height of the bitmap on the destination; a handle to a source DC; X and Y coordinates for the logical origin of the bitmap on the source; and, most interestingly, a Raster Operation (ROP) code.

The ROP code determines which of 256 possible Boolean functions will be performed on three sets of bits to produce the final image. The first set of bits is called the "Pattern." The Pattern is a bitmap which is the currently selected "brush" in the destination display context. The other two sets of bits are the bitmaps selected in the source and destination display contexts.

Of the 256 possible ROP code constants, 15 have common names, and it is good form to use the name in place of the constant if one exists. Not surprisingly, the named ROP codes are the ones most frequently used. There are some cases in which it is pretty obvious which one will do the job. For instance, SRCCOPY will simply "wallpaper" the source bitmap on to the destination at the location you specify. For the occasions when it is not so clear which Boolean function will produce the result you want, here is how to determine the appropriate ROP code.

Forming the Raster Operation Code

As an example, we'll find a ROP code which will cause all the pixels outside the combination of Area 1 and Area 2 to be black, and all the interior pixels to be white. First construct the following truth table in Table 1(a).

Table 1: Truth tables for interior pixels

  (a) Pattern:        1    1    1    1    0    0    0    0
      Source:         1    1    0    0    1    1    0    0
      Destination:    1    0    1    0    1    0    1    0

  (b) Pattern:        1    1    1    1    0    0    0    0
      Source:         1    1    0    0    1    1    0    0
      Destination:    1    0    1    0    1    0    1    0
                   ---------------------------------------

      Result:              1              1

  (c) Pattern:        1    1    1    1    0    0    0    0
      Source:         1    1    0    0    1    1    0    0
      Destination:    1    0    1    0    1    0    1    0
                   ---------------------------------------

      Result:              1    1    1         1    1    1

  (d) Pattern:        1    1    1    1    0    0    0    0
      Source:         1    1    0    0    1    1    0    0
      Destination:    1    0    1    0    1    0    1    0
                   ---------------------------------------

      Result:         0    1    1    1    0    1    1    1

You may wonder about the content and meaning of the bit patterns in this table. (I am always curious when I come across a new "sacred number.") The patterns have no physical meaning, but do result in 256 unique ROP code indexes across a new "sacred number.") The patterns have no physical meaning, but do result in 256 unique ROP code indexes upon Boolean combination. The two hex digits that result from the combination of the Pattern, Source, and Destination bit strings give us an index into the table of ternary raster operation codes in the Windows Software Development Kit Reference literature.

Recall that each of the areas is black and the background is white. In Table 1(b), 0 is black and 1 is white. Where source and destination are both 0 (black), we need a 1 (white) in the Result, because such a bit falls inside both areas; refer to Table 1(c). Where either source or destination are 0 (black), we need a 1 (white) in the Result, because such a bit falls inside one of the areas as in Table 1(d).

Where source and destination are both 1 (white), we need a 0 (black) in the Result, because such a bit falls outside both of the areas. Our Result is 77H.

Our result is called the "Boolean Function Number." We can find this number in the table of ternary raster operation codes, volume 2, chapter 11 of the Windows SDK Reference. The full ROP code, 7700e6H, and a Reverse Polish description of the Boolean function (DSan) are listed there. For this code, there is no common name.

For each of the cases in the paint switch, blackening the bits we want to count is just a matter of determining the ROP code and BitBlting the two images.

Counting the bits is now fairly straight-forward. When we call the function FindArea, we pass the handle to the visual DC because the pixels we need to count have been BitBlted to the screen. We create a single-plane bitmap so that the final array of bits will have a one-to-one correspondence with the pixels they represent. Because we created our other bitmaps to be compatible with the visual DC, they could have had some other color organization (multiple bits per pixel or more than one color plane, for example). Now we want them to be translated to a monochrome bitmap that has the dimensions of the client area of our window. In the translation that will take place, if the source was organized as a color bitmap, all source pixels which are the same color as the source background will be white in the monochrome destination. All other pixels will be black. For our example, this is just right. BitBlt performs this translation when it detects a difference in the color organization of the two bitmaps.

Next, we get information about the bitmap we just created and put it in the BITMAP structure we call bmArea. (In this case, this call was not strictly necessary.) We determine how much space we need to retrieve a copy of the bits and make a call to LocalAlloc. After we lock the block and get a near pointer to it, we retrieve our data with a call to GetBitmapBits.

Counting the Bits

A little counting and a little multiplying and we're home free. The procedure _COUNT_BITS, shown in Listing One (page 14), is an assembly language routine that does the following things: It takes the bits in word size hunks; it shifts the word left one bit at a time; and for each 0 (or black) bit that "falls off" the top and into the Carry Flag, it increments an accumulator. The value of the accumulator is passed back to the caller as SetBits. I'll be the first to admit that this isn't reminiscent of rocket science or Handel's Water Music. It does accomplish a couple of things, though. Counting bits is the sort of job where performance can be drastically improved through the use of assembly language, and it's a tool we shouldn't avoid when its use is justified. Second, you'll notice that we went to some length to avoid changing any segment registers. It was all right to alter and restore segment registers in previous versions of Windows, but this is no longer true. With respect to mixed-language Windows programming, it's worthwhile to follow most of the rules your mother instructed you to practice: Be careful with things that don't belong to you (segment registers), pick up after yourself (make sure you clear the stack of local data and saved registers), and don't be getting into things you haven't asked to borrow (by writing beyond the end of allocated memory objects). After we count the bits and multiply by fAreaPerPixel, we report our estimate using a MessageBox. Whew!

Closings and Caveats

There we have the concept of finding areas with raster operations. With a little adjustment, the strategy should work in about any environment. Of course, the success of this approach is highly dependent on the original coordinate data. The larger the areas, the less accurate the estimates. Much digital map data is geometrically manipulated to optimize for its use in all sorts of different applications. Inherently, when we try to measure flat squares on our somewhat spherical and bumpy earth, things get complicated. Not all cartographic projections would yield coordinates that would work well in this scheme. Another deficiency of this example is the expectation of monochrome areas. Dealing with colored areas would be more useful, but more involved. Resizing the window and round-off error will result in minor changes in the reported area.

Acknowledgments

Thanks to Stan Moll of the U.S. Geological Survey/National Mapping Division, for spending a morning rounding up digital data for Area 1 and Area 2. Also many thanks to Bill Clark, colleague and professor of Computer Science at the University of Alaska, for his advice and service as a sounding board. And, as are many Windows programmers, I am greatly in the debt of a wise stranger, Charles Petzold, author of the indispensable Programming Windows 3.


_QUICK APPROXIMATIONS OF POLYGONAL AREAS USING BITBLT_
by Nancy Nicolaisen


[LISTING ONE]
<a name="02b5_000d">


; Parameters:
; BP+6 = Offset to bits
; BP+8 = Bitmap Height in scan lines
; BP+10 = Bitmap width in pixels
; Local Data
; BP-2 = hiword of the bit count
; BP-4 = loword of the bit count

.8086
.MODEL   MEDIUM
memM   EQU 1
INCLUDE CMACROS.INC

.CODE
    PUBLIC  _COUNT_BITS

_COUNT_BITS  PROC
      PUSH   BP      ; Preserve BP
      MOV   BP, SP      ; Set stack frame pointer
      SUB   SP, 4      ; This will hold our byte count
      PUSH   DI      ; Preserve DI
      PUSH   SI      ; Preserve SI

      SUB   AX, AX      ;Zero the accumulator reg
      MOV   [BP-2], AX   ;Zero the local
      MOV   [BP-4], AX   ; storage
      MOV   DI, [BP+8]   ;Height of the bitmap in scan lines
      MOV   SI, [BP+6]   ;DS:offset to bits

scanning_line:   MOV   DX, [BP+10]   ;Number of bits to check in each
               ; scan line
scanning_bytes: MOV   BX, [SI]
      XCHG   BH, BL      ;Get a word
      MOV   CL, 16      ;prepare to scan 16 bits
shifting_bits:   SAL   BX,1      ;shift most significant remaining bit
      JC   shift_again   ;if it was set, shift again
      INC   AX      ;increment ax for each 0 bit

shift_again:   DEC   DX      ;Decrement bits/line
      JZ   new_line   ;Process a new scan line?
      DEC   CL      ;do we have bits left in this word?
      JNZ   shifting_bits   ;keep shifting this word
      INC   SI      ;if not, advance to the
      INC   SI      ; next word
      JMP   scanning_bytes   ; and look for black bits

new_line:   ADD   [BP-4], AX   ;Add this line's total to our
      ADC   WORD PTR [BP-2], 0
               ; accumulator

      DEC   DI      ;Decrement the line counter
      JZ   pass_bit_count   ;If were done, do exit things
      INC   SI      ;Else bump the pointer to bits
      INC   SI      ; past the word we just scanned
               ; and any pad bytes
      SUB   AX, AX      ;Zero the accumulator
      JMP   scanning_line   ;Do the next line

pass_bit_count:
      POP   SI      ;Restore SI
      POP   DI      ;Restore DI
      POP   AX      ;Loword of bitcount
      POP   DX      ;Hiword of bitcount
      POP   BP      ;Restore BP

      RET         ;Return the bit count in DX:AX
               ; and let the caller clear the stack

_COUNT_BITS   ENDP
END





<a name="02b5_000e">
<a name="02b5_000f">
[LISTING TWO]
<a name="02b5_000f">

/***************************************************************************/
/*         I N C L U D E   F I L E S            */
/***************************************************************************/

#include "\windev\include\windows.h"
#include "areas.h"


/***************************************************************************/
/*   T H E   P R O G R A M ' S   G L O B A L   V A R I A B L E S        */
/***************************************************************************/

static   HANDLE   hInst;        /*  Data that can be referenced thruout  */
static   HWND   hWnd;        /*   the program, but is not normally   */
long   float   fAreaPerPixel;
HDC      hDCMem;
HBITMAP    hOldBitmap;
/***************************************************************************/
/*          M A I N   P R O G R A M            */
/***************************************************************************/

int PASCAL WinMain (hInstance,
          hPrevInstance,
          lpszCmdLine,
          cmdShow)

HANDLE hInstance, hPrevInstance;
LPSTR  lpszCmdLine;           /*  Length of the command line.  */
int    cmdShow;            /*  Iconic or Tiled when start.  */
{
  MSG   msg;

  hInst = hInstance;
 if( hPrevInstance )
   {
   return (FALSE);
   }

  Init (hInstance, cmdShow);         /* Initialization rtn.*/

  while                /*  The main loop:      */
    (GetMessage((LPMSG)&msg, NULL, 0, 0))   /*  (terminated by a QUIT) */
      {
    TranslateMessage(&msg);      /*  Have Windows translate */
    DispatchMessage(&msg);       /*  Have Windows give mess */
                  /*  to the window proc.    */
      }
  exit(msg.wParam);            /*  End of the program.    */
}

/***************************************************************************/
/*             I N I T I A L I Z A T I O N            */
/***************************************************************************/

int  FAR PASCAL Init (hInstance, cmdShow)

HANDLE hInstance;
int    cmdShow;
{
    WNDCLASS   rClass;            /* Window class structure.      */
    int        FullScreenX;
    int        FullScreenY;

    rClass.lpszClassName = (LPSTR) "NN:AREA";
    rClass.hInstance    = hInstance;
    rClass.lpfnWndProc    = WindowProc;
    rClass.hCursor    = LoadCursor (NULL, IDC_ARROW) ;
    rClass.hIcon    = LoadIcon (hInstance, "AREAS");
    rClass.lpszMenuName  = (LPSTR) "AreaFinder";
    rClass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
    rClass.style    = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    rClass.cbClsExtra    = 0;
    rClass.cbWndExtra    = sizeof( WORD );

    RegisterClass ( &rClass); /*  Register the class. */

    hInst = hInstance;
    FullScreenY = GetSystemMetrics( SM_CYFULLSCREEN );
    FullScreenX = GetSystemMetrics( SM_CXFULLSCREEN );
    hWnd = CreateWindow((LPSTR) "NN:AREA",  /*   Window class name.    */
         "Using BitBlt to Estimate Areas - Dr. Dobbs",
                     /* Window Title         */
         WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
                     /*  Type of window.      */
         0,            /*  Where the window should  */
         0,            /*  go when the app opens... */
         (FullScreenX  / 16 ) * 16,
                     /* Make a scan line fill an  */
                     /*  even # words         */
         FullScreenY,         /*            */
         NULL,            /*  No parent for this wind  */
         NULL,            /*  Use the class menu.      */
         hInstance,         /*  Who created this window. */
         NULL            /*  No params to pass on.    */
             ) ;
    ShowWindow( hWnd, cmdShow );
    return TRUE;
 }


/***************************************************************/
/*        T H E    W I N D O W   P R O C E D U R E             */
/***************************************************************/


long FAR PASCAL WindowProc (hWnd, message, wParam, lParam )

HWND      hWnd;             /*  Handle of the window  */
unsigned   message;             /*  Message type      */
WORD      wParam;             /*  Message 16 bit param  */
LONG      lParam;             /*  Message 32 bit param  */
{


    switch (message)             /*  Check the mess type   */
   {
   case WM_COMMAND:
     switch(wParam)
      {             /* Store wParam in the    */
      case ID_AREA1:          /*  WindowWord and tell   */
      case ID_AREA2:          /*   the paint proc about */
      case ID_UNION:          /*   it...         */
      case ID_INTERSECTION:
      case ID_EXCLUSIVE:
      case ID_OUTSIDE:
      case ID_SHOWPOLYS:
       SetWindowWord(hWnd, 0, wParam );
       InvalidateRect( hWnd, NULL, TRUE );
       UpdateWindow( hWnd );
       break;

      default:
         break;
      }
     break;

   case WM_CREATE:
       InvalidateRect( hWnd, NULL, TRUE );
       UpdateWindow( hWnd );
       break;

   case WM_PAINT:
       PaintAreaWindow( hWnd );
       break;

   case WM_SIZE:                /* Dont let the window change */
       break;                /*  size...           */

   case WM_DESTROY:
       PostQuitMessage(0);           /*    send yourself a QUIT  */
       break;                /*    message.         */

   default:
       return(DefWindowProc(hWnd, message, wParam, lParam));
       break;
   }
    return(0L);
}
RECT        rWorkRect;
/***************************************************************************/
/*        T H E   P A I N T   P R O C E D U R E          */
/***************************************************************************/

int FAR PASCAL PaintAreaWindow (hWnd)

HWND         hWnd;             /*   Handle of the window.      */
{

PAINTSTRUCT  ps;
HDC        hDC;
HANDLE        hArea1Meta;
HANDLE        hArea2Meta;
HBITMAP      hArea1;
HBITMAP      hArea2;


WORD        WhatToEstimate;


hDC = BeginPaint( hWnd, &ps);
GetClientRect( hWnd, &rWorkRect );
SetMapMode( hDC, MM_ANISOTROPIC );    /* X and Y are dimensionally  equal */
SetViewportOrg( hDC, 0, rWorkRect.bottom  );
                /* The viewport origin is at the    */
                /*  left corner of the screen...    */
SetViewportExt( hDC, rWorkRect.right, -rWorkRect.bottom );
                /*  X increases to the right and Y  */
                /*   increases going up...        */
SetWindowOrg( hDC, X_ORIGIN, Y_ORIGIN );
SetWindowExt( hDC, X_EXTENT, Y_EXTENT );
                /* Logical dimensions depend on the */
                /*  data which defines the areas.   */
                /*  The constants are defined in    */
                /*  Areas.h              */
hDCMem = CreateCompatibleDC( hDC );
SetMapMode( hDCMem, MM_ISOTROPIC );

SetViewportOrg( hDCMem, 0, rWorkRect.bottom  );
SetViewportExt( hDCMem, rWorkRect.right, -rWorkRect.bottom );
SetWindowOrg( hDCMem, X_ORIGIN, Y_ORIGIN );
SetWindowExt( hDCMem, X_EXTENT, Y_EXTENT );
                /* Create a memory display context  */
                /*  that simulates the visible DC...*/

hArea1 =
 CreateCompatibleBitmap( hDCMem, rWorkRect.right , rWorkRect.bottom );
hOldBitmap = SelectObject( hDCMem, hArea1 );
                /* Create a bitmap with the same    */
                /*  organization as this device and */
                /*  select it into the memory DC... */
hArea1Meta = GetMetaFile( "Area1.bas" );
SaveDC( hDCMem );
PlayMetaFile( hDCMem, hArea1Meta );
RestoreDC( hDCMem, -1 );
DeleteMetaFile( hArea1Meta );

hArea2 =
 CreateCompatibleBitmap( hDCMem, rWorkRect.right, rWorkRect.bottom );
SelectObject( hDCMem, hArea2 );
hArea2Meta = GetMetaFile( "Area2.bas" );   /* For convenience, we'll construct */
SaveDC( hDCMem );            /*  our area bitmaps using pre-     */
PlayMetaFile( hDCMem, hArea2Meta );      /*  recorded metafiles.  Since the  */
RestoreDC( hDCMem, -1 );         /*  metafile can change the attrib- */
DeleteMetaFile( hArea2Meta );         /*  utes of the DC, its often a good*/
                  /*  practice to use the context     */
                  /*  stack to preserve the DC before */
                  /*  playing the metafile, and       */
                  /*  restore it afterward...          */
fAreaPerPixel =
 ( (float)X_EXTENT / (float)rWorkRect.right ) *
 ( (float)Y_EXTENT / (float)rWorkRect.bottom );
                /* Calculate the area of 1 Pixel... */


WhatToEstimate = GetWindowWord( hWnd, 0 );
switch( WhatToEstimate )
 {
  case ID_AREA1:
   SelectObject( hDCMem, hArea1 );
   DeleteObject( hArea2 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea1 );
   FindArea( hDC, &rWorkRect );
   break;            /* Estimate the area of Area 1        */

  case ID_AREA2:
   DeleteObject( hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   FindArea( hDC, &rWorkRect );    /* Estimate the area of Area 2...    */
   break;

  case ID_UNION:
   SelectObject( hDCMem, hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hArea2 );
   DeleteObject( hArea1 );

   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCAND );
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   FindArea( hDC, &rWorkRect );     /* Estimate the area of the union   */
   break;

  case ID_INTERSECTION:
   SelectObject( hDCMem, hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hArea2 );
   DeleteObject( hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCPAINT );
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   FindArea( hDC, &rWorkRect );    /* Estimate the area of the intersec-
                   tion... */
   break;

  case ID_EXCLUSIVE:
   SelectObject( hDCMem, hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hArea2 );
   DeleteObject( hArea1 );

   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, 0x990066 );
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   FindArea( hDC, &rWorkRect );    /* Estimate the area of an exclusive
                   combination... */
   break;

  case ID_OUTSIDE:
   SelectObject( hDCMem, hArea1 );
   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   SelectObject( hDCMem, hArea2 );
   DeleteObject( hArea1 );

   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, 0x7700e6 );
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   FindArea( hDC, &rWorkRect );    /* Estimate the area outside the
                   combined areas...*/
   break;

  case ID_SHOWPOLYS:
   SelectObject( hDCMem, hArea1 );

   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY);
   MessageBox(hWnd, "This is Area 1... ", "Areas", MB_OK );
   SelectObject( hDCMem, hArea2 );
   DeleteObject( hArea1 );

   BitBlt( hDC, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
      hDCMem, X_ORIGIN, Y_ORIGIN, SRCCOPY );
   SelectObject( hDCMem, hOldBitmap );
   DeleteObject( hArea2 );
   MessageBox(hWnd, "This is Area 2... ", "Areas", MB_OK );
               /* Lets have a look at the areas... */
   break;


 default:
   break;
 }
DeleteDC( hDCMem );     /* Always relinquish GDI leftovers!! */
DeleteObject( hOldBitmap );
ValidateRect( hWnd, NULL );
           /* Validate the client area...       */
EndPaint (hWnd, &ps);     /* Finished painting for now.        */
SetWindowWord(hWnd, 0, NULL );
return TRUE;
}

/***************************************************************************/
/*   F I N D   A R E A                     */
/***************************************************************************/
void PASCAL FindArea( hDC, lprWork )

 HDC          hDC;
 LPRECT        lprWork;

{

 HBITMAP       hArea;
 HANDLE        hAreaMemory;
 BITMAP        bmArea;
 PSTR          pAreaBits;
 unsigned int  NumberBytes;


 long   float  fArea;
 long          SetBits;
 char          szApproxArea[12];



 hArea = CreateBitmap( lprWork->right, lprWork->bottom, 1, 1, NULL );
                /* Create a monochrome bitmap with the
               same dimensions as the client area...
                */
 SelectObject( hDCMem, hArea );

 BitBlt( hDCMem, X_ORIGIN, Y_ORIGIN, X_EXTENT, Y_EXTENT,
    hDC, X_ORIGIN, Y_ORIGIN, SRCCOPY);
                /* Select it into the memory DC and copy
               the client area to it...
                */
 GetObject( hArea, sizeof( BITMAP ), &bmArea );
                /* Get the dimensions and color organization
               information about the monochrome bitmap...
                */

 NumberBytes = bmArea.bmPlanes * bmArea.bmHeight * bmArea.bmWidthBytes;
 hAreaMemory = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, NumberBytes );
 if( hAreaMemory == NULL )
  {
   MessageBox(hWnd, "Try closing other windows or resizing Areas...","Unable to allocte memory!", MB_OK | MB_ICONHAND );
   return;
  }
 pAreaBits = LocalLock( hAreaMemory );
 GetBitmapBits( hArea, (DWORD)NumberBytes, (LPSTR)pAreaBits );
                /* Allocate memory and get bits... */
 SetBits = COUNT_BITS( pAreaBits, bmArea.bmHeight, bmArea.bmWidth);

               /* Count the Black ( 0H ) bits...  */
 LocalUnlock( hAreaMemory );      /* Release memory...          */
 LocalFree( hAreaMemory );
 SelectObject( hDCMem, hOldBitmap );
 DeleteObject( hArea );
               /* Delete the monochrome bitmap... */
 fArea = SetBits * fAreaPerPixel;
 sprintf( szApproxArea, "%.2f", fArea );
 MessageBox(hWnd, szApproxArea, "Approximate area in square meters... ", MB_OK );
                /* Calculate and display area... */
 return;
}


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.