_QUICK APPROXIMATIONS OF POLYGONAL AREAS USING BITBLT_ by Nancy Nicolaisen [LISTING ONE] ; 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 [LISTING TWO] /***************************************************************************/ /* 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; }