For the MM_TEXT mapping mode, the default origins and extents are shown below:
Window origin: | (0, 0) | Can be changed |
Viewport origin: | (0, 0) | Can be changed |
Window extent: | (1, 1) | Cannot be changed |
Viewport extent: | (1, 1) | Cannot be changed |
The ratio of the viewport extent to the window extent is 1, so no scaling is performed between logical coordinates and device coordinates. The formulas shown on the preceding page reduce to these:
xViewport = xWindow - xWinOrg + xViewOrg
This mapping mode is called a ”text“ mapping mode not because it's most suitable for text but because of the orientation of the axes. We read text from left to right and top to bottom, and MM_TEXT defines values on the axes to increase the same way:
Windows provides the functions SetViewportOrg and SetWindowOrg for changing the viewport and window origins. These functions have the effect of shifting the axis so that the logical point (0, 0) no longer refers to the upper left corner. Generally, you'll use either SetViewportOrg or SetWindowOrg but not both.
Here's how these functions work: If you change the viewport origin to (xViewOrg, yViewOrg), then the logical point (0, 0) will be mapped to the device point (xViewOrg, yViewOrg). If you change the window origin to (xWinOrg, yWinOrg), then the logical point (xWinOrg, yWinOrg) will be mapped to the device point (0, 0), which is the upper left corner. Regardless of any changes you make to the window and viewport origins, the device point (0, 0) is always the upper left corner of the client area.
For instance, suppose your client area is cxClient pixels wide and cyClient pixels high. If you want to define the logical point (0, 0) to be the center of the client area, you can do so by calling:
SetViewportOrg (hdc, cxClient / 2, cyClient / 2) ;
The arguments to SetViewportOrg are always in terms of device units. The logical point (0, 0) will now be mapped to the device point (cxClient / 2, cyClient / 2). Now you use your client area as if it had the following coordinate system:
The logical x-axis ranges from -cxClient / 2 to +cxClient / 2, and the logical y-axis ranges from -cyClient / 2 to +cyClient / 2. The lower right corner of the client area is the logical point (cxClient / 2, cyClient / 2). If you want to display text starting at the upper left corner of the client area, which is the device point (0, 0), you need to use negative coordinates:
TextOut (hdc, -cxClient / 2, -cyClient / 2, "Hello", 5) ;
You can achieve the same result with SetWindowOrg as you did with SetViewportOrg:
SetWindowOrg (hdc, -cxClient / 2, -cyClient / 2) ;
The arguments to SetWindowOrg are always in terms of logical units. After this call, the logical point (-cxClient / 2, -cyClient / 2) is mapped to the device point (0, 0), the upper left corner of the client area.
What you probably don't want to do (unless you know what's going to happen) is to use both functions together:
SetViewportOrg (hdc, cxClient / 2, cyClient / 2) ;
SetWindowOrg (hdc, -cxClient / 2, -cyClient / 2) ;
This means that the logical point (-cxClient / 2, -cyClient / 2) is mapped to the device point (cxClient / 2, cyClient / 2), giving you a coordinate system that looks like this:
You can obtain the current viewport and window origins from these functions:
dwViewOrigin = GetViewportOrg (hdc) ;
dwWindowOrigin = GetWindowOrg (hdc) ;
Both functions return DWORDs (unsigned longs). The x-origin is in the low word and the y-origin is in the high word. You can use the LOWORD and HIWORD macros to extract these two values from the DWORD. The values returned from GetViewportOrg are in device coordinates; the values returned from GetWindowOrg are in logical coordinates.
You might want to change the viewport or window origin to shift display output within the client area of your window—for instance, in response to scroll bar input from the user. Changing the viewport or window origin doesn't shift the display output immediately, of course. You change the origin and then repaint the display. For instance, in the SYSMETS2 program in Chapter 2, we used the nVscrollPos value (the current position of the vertical scroll bar) to adjust the y-coordinates of the display output:
case WM_PAINT:
BeginPaint (hwnd, &ps) ;
for (i = 0 ; i < NUMLINES ; i++)
{
y = cyChar * (1 - nVscrollPos + i) ;
[display text]
}
EndPaint (hwnd, &ps) ;
return 0 ;
We can achieve the same result using SetWindowOrg:
case WM_PAINT:
BeginPaint (hwnd, &ps) ;
SetWindowOrg (ps.hdc, 0, cyChar * nVscrollPos) ;
for (i = 0 ; i < NUMLINES ; i++)
{
y = cyChar * (1 + i) ;
[display text]
}
EndPaint (hwnd, &ps) ;
return 0 ;
Now the calculation of the y-coordinate for the TextOut functions doesn't require the nVscrollPos value. This means you can put the text output functions in a subroutine and not have to pass the nVscrollPos value to the subroutine, because we adjust the display of the text by changing the window origin.
If you have some experience working with rectangular (or Cartesian) coordinate systems, moving the logical point (0, 0) to the center of the client area as we did earlier may have seemed a reasonable action. However, there's a slight problem with the MM_TEXT mapping mode: A Cartesian coordinate system defines values on the y-axis to increase as you move up the axis, whereas MM_TEXT defines the values to increase as you move down. In this sense, MM_TEXT is an oddity, and these next five mapping modes do it correctly.