SOME MISCELLANEOUS GDI FUNCTIONS

A few additional drawing functions don't fit into any convenient categories. These include FloodFill, DrawIcon, ScrollWindow, ScrollDC, LineDDA, and LineProc.

FloodFill fills in an area with the current brush. The syntax is:

FloodFill (hdc, xStart, yStart, rgbColor) ;

This is the function that the Windows PAINTBRUSH program calls when you use the ”paint roller“ icon. Starting at the logical point (xStart, yStart), the function fills in an area until it encounters a boundary of rgbColor.

FloodFill doesn't work if the point (xStart, yStart) is rgbColor itself or if the point is outside the clipping region. If you use FloodFill during normal repainting of your client area, you might want to invalidate the entire client area before calling BeginPaint to be sure that (xStart, yStart) is in the clipping region.

The ExtFloodFill function has the following syntax:

ExtFloodFill (hdc, xStart, yStart, rgbColor, wFill) ;

ExtFloodFill is an extended version of FloodFill that (depending on the last parameter) can fill to a boundary or over a surface the color of rgbColor.

DrawIcon draws an icon on the display:

DrawIcon (hdc, xStart, yStart, hIcon) ;

This function works only in the MM_TEXT mapping mode. (The DrawIcon function appears in Chapter 9.)

Both ScrollWindow and ScrollDC scroll part of the window's client area. We used ScrollWindow in the SYSMETS3 program in Chapter 2. It's not a GDI function, so it always uses device units. The general syntax is:

ScrollWindow (hwnd, xScroll, yScroll, &rectScroll, &rectClip) ;

Note that the first parameter is a handle to a window rather than to a device context. The section of the client area indicated by the rectScroll rectangle structure is shifted right xScroll pixels and down yScroll pixels. These two parameters can be negative if you use the function to scroll to the left or up.

Only the area within the rectClip rectangle is affected by this scrolling, so it makes sense to set rectClip only to be the same rectangle as rectScroll, a larger rectangle, or NULL. If you want the area within rectScroll to be scrolled without affecting anything outside the rectangle, set rectClip to equal rectScroll. If it is acceptable for the area within rectScroll to be shifted outside the rectangle, set rectClip to NULL. The area uncovered by the scrolling is invalidated and generates a WM_PAINT message. Because ScrollWindow is not a GDI function, it can't perform clipping and will also scroll dialog boxes or child windows that may be covering the surface of the client area.

ScrollDC is a GDI function and works differently. The syntax is:

ScrollDC (hdc, xScroll, yScroll, &rectScroll, &rectClip,

hRgnUpdate, &rectUpdate) ;

When not set to NULL, the last two parameters return information to the program. If you pass a region handle to Windows in the sixth parameter, Windows sets the region to the invalid region uncovered by the scroll. If you pass a pointer to a RECT structure in the last parameter, Windows sets the structure to indicate the invalid rectangle uncovered by the scroll.

The LineDDA function calculates all the points on a straight line that connects two given points. (DDA stands for ”digital differential analyzer.“) The syntax is:

LineDDA (xStart, yStart, xEnd, yEnd, lpfnLineProc, lpData) ;

LineDDA requires a call-back function of the form:

void FAR PASCAL LineProc (x, y, lpData)

short x, y ;

LPSTR lpData ;

{

[other program lines]

}

LineProc must be included in the EXPORTS section of your module definition (.DEF) file. The lpfnLineProc parameter you pass to LineDDA must be the result of a MakeProcInstance call:

lpfnLineProc = MakeProcInstance (LineProc, hInstance) ;

When you call LineDDA, Windows calls LineProc once for each point on the line connecting (xStart, yStart) and (xEnd, yEnd). The point is indicated by the x and y parameters to LineProc. LineProc also receives the far pointer to programmer-defined data that you supply in the LineDDA call.

Note that LineDDA does not require a handle to a device context. The function is simply a line calculation algorithm that gives your program each point it calculates. What you do with these points is up to you. In the LINEDDA program, shown in Figure 12-19, I chose to draw a rectangle and connect the corners of the rectangle with the corners of the client area. Not satisfied with the mundane dotted and dashed line styles that Windows allows one to use with more ease, I chose to draw the lines as a series of tiny ellipses. The result is shown in Figure 12-20 on page 597.

LINEDDA.MAK

#-----------------------

# LINEDDA.MAK make file

#-----------------------

linedda.exe : linedda.obj linedda.def

link linedda, /align:16, NUL, /nod slibcew libw, linedda

rc linedda.exe

linedda.obj : linedda.c

cl -c -Gsw -Ow -W2 -Zp linedda.c

LINEDDA.C

/*----------------------------------------

LINEDDA.C -- LineDDA Demonstration

(c) Charles Petzold, 1990

----------------------------------------*/

#include <windows.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

void FAR PASCAL LineProc (short, short, LPSTR) ;

HANDLE hInst ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName[] = "LineDDA" ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = WndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = NULL ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hInst = hInstance ;

hwnd = CreateWindow (szAppName, "LineDDA Demonstration",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, nCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static FARPROC lpfnLineProc ;

static short cxClient, cyClient, xL, xR, yT, yB ;

HDC hdc ;

PAINTSTRUCT ps ;

switch (message)

{

case WM_CREATE :

lpfnLineProc = MakeProcInstance (LineProc, hInst) ;

return 0 ;

case WM_SIZE :

xR = 3 * (xL = (cxClient = LOWORD (lParam)) / 4) ;

yB = 3 * (yT = (cyClient = HIWORD (lParam)) / 4) ;

return 0 ;

case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;

LineDDA (xL, yT, xR, yT, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (xR, yT, xR, yB, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (xR, yB, xL, yB, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (xL, yB, xL, yT, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (0, 0, xL, yT, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (cxClient, 0, xR, yT, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (cxClient, cyClient, xR, yB, lpfnLineProc, (LPSTR) &hdc) ;

LineDDA (0, cyClient, xL, yB, lpfnLineProc, (LPSTR) &hdc) ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

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

}

void FAR PASCAL LineProc (short x, short y, LPSTR lpData)

{

static short nCounter = 0 ;

if (nCounter == 2)

Ellipse (* (HDC far *) lpData, x - 2, y - 2, x + 3, y + 3) ;

nCounter = (nCounter + 1) % 4 ;

}

LINEDDA.DEF

;------------------------------------

; LINEDDA.DEF module definition file

;------------------------------------

NAME LINEDDA

DESCRIPTION 'LineDDA Demonstration (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

LineProc

The LineDDA function is called eight times during processing of the WM_PAINT message, once for each of the eight lines. The lpData parameter is the address of the handle to the device context.

The LineProc function is short:

void FAR PASCAL LineProc (x, y, lpData)

short x, y ;

LPSTR lpData ;

{

static short nCounter = 0 ;

if (nCounter == 2)

Ellipse (* (HDC far *) lpData, x - 2, y - 2, x + 3, y + 3) ;

nCounter = (nCounter + 1) % 4 ;

}

Note that the nCounter variable is defined as static so that its value is preserved between LineProc calls. It cycles through the values 0, 1, 2, and 3. When the value is 2, LineProc draws an ellipse centered on the point.