Brush Alignment

When Windows fills in an area with a brush, it repeats the 8-by-8 bitmap both horizontally and vertically. This brush's appearance can vary slightly, depending on how Windows aligns the upper left corner of the bitmap with the display surface. The attribute in the device context that determines this alignment is called the ”brush origin.“ This attribute is always expressed in terms of screen coordinates. If you obtain a device context using BeginPaint or GetDC, the brush origin is initially set to the upper left corner of your window's client area. If the client area begins 30 pixels from the left of the screen and 20 pixels from the top of the screen, the brush origin is set to the point (30, 20).

Whenever Windows uses a brush within the client area, the upper left corner of the brush's bitmap coincides with those client-area device points where both x and y are multiples of 8. Let's take an example involving a hatch brush with style HS_FDIAGONAL, which looks like this:

In MM_TEXT mode, when you draw a rectangle that is filled in with the HS_FDIAGONAL brush, the downward hatch line will intersect the upper left corner of the rectangle if this corner is at the logical point (8, 8). However, the hatch line will be aligned 4 pixels from the top of the corner if the rectangle begins at the logical point (8, 4).

In most cases, adjusting the brush origin is an unnecessary refinement to your drawing, but sometimes you'll want to do it. The process involves three steps:

1.Call UnrealizeObject for the brush. (Do not call UnrealizeObject for stock brushes.)

2.Set the brush origin with SetBrushOrg, remembering to use screen coordinates.

3.Select the brush into the device context using SelectObject.

One situation in which you'll need to change the brush origin is when you want the background of a child window to blend in with the background of its parent window. When you obtain a handle to the device context for the child window, the brush origin will be the upper left corner of the child window. You need to change the brush origin to be the upper left corner of the parent window. (We did this when coloring child window controls in Chapter 6.) In this case, you're changing the brush origin in one device context so that it coincides with the brush origin in another device context.

You'll also need to change brush origins when you draw several figures sharing the same device context but you don't want the brushes to coincide. For instance, suppose you want to use brushes to color the bars of a bar chart. If you didn't care about brush alignment, you might draw each of the bars using a function that looks like this:

void DrawBar1 (hdc, xLeft, yTop, xRight, yBottom, hBrush)

HDC hdc ;

short xLeft, yTop, xRight, yBottom ;

HBRUSH hBrush ;

{

HBRUSH hBrushOriginal ;

hBrushOriginal = SelectObject (hdc, hBrush) ;

Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;

SelectObject (hdc, hBrushOriginal) ;

}

This function simply selects the brush handle passed as a function parameter (saving the handle to the brush originally selected), draws a rectangle, and then selects the original brush back into the device context. If a program used this routine to draw several adjacent bars that it filled in with the HS_FDIAGONAL brush, the result would look like Figure 12-15. Notice that the hatch lines for all three bars align, which has the unfortunate effect of drawing the eye downward from left to right.

To avoid this effect, you need to align the brush with each bar individually, which you can accomplish with the following function:

void DrawBar2 (hwnd, hdc, xLeft, yTop, xRight, yBottom, hBrush)

HWND hwnd ;

HDC hdc ;

short xLeft, yTop, xRight, yBottom ;

HBRUSH hBrush ;

{

HBRUSH hBrushOriginal ;

POINT pt ;

UnrealizeObject (hBrush) ;

pt.x = xLeft ;

pt.y = yTop ;

LPtoDP (hdc, &pt, 1) ;

ClientToScreen (hwnd, &pt) ;

SetBrushOrg (hdc, pt.x, pt.y) ;

hBrushOriginal = SelectObject (hdc, hBrush) ;

Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;

SelectObject (hdc, hBrushOriginal) ;

}

You ”unrealize“ the brush by calling UnrealizeObject and then set the brush origin to the upper left corner of the bar that's being drawn. This requires translating the upper left corner of the bar to client-area coordinates using LPtoDP and then to screen coordinates using ClientToScreen. The result is shown in Figure 12-16. Now the hatch lines begin in the upper left corner of each bar and do not align from one bar to the next.