Wednesday, 27 January 2010

GDI Mapping Modes Explaind

GDI Mapping Modes Explained

Date: 8 May 1997
Author: Rich Goldstein, MD (

NOTE: Also, take a look this article (


Each device context (DC, represented by TDC and derivatives in OWL) has the ability to maintain a coordinate system separate and distinct from the device it represents. So while the display may be a 640x480 or 800x600 or whatever pixels in dimension, we can tell the DC that the coordinate space uses some other units.

The result is the existence of 2 coordinate systems: the DEVICE coordinate system, and the LOGICAL coordinate system.

In general, DEVICE coordinates are established by the device or it's related drivers. So for the monitor, the display driver determines the DEVICE coordinates (0,0 in the top left, width,height in pixels in the lower right). These are rarely if ever changed programmatically (the exceptions, of course, include changing screen or printer resolution, or paper orientation, etc.).

The LOGICAL coordinates relate to the device context (DC) and are established by the mapping mode, viewport origin and extents, and window origin and extents. All DC related functions accept LOGICAL coordinates, unless explicitly stated (e.g. DPtoLP, which converts DEVICE coordinates to LOGICAL coordinates).

The system maps your LOGICAL coordinates to the DEVICE coordinates using the viewport/window origins and extents.



OK, let's start with the window, which is expressed in LOGICAL coordinates.

SetWindowOrg() tells the DC the LOGICAL point that maps to the DEVICE point (0,0). So if you call SetWindowOrg(100,100) for a window, the LOGICAL point (100,100) occurs in the top left corner. (Hold on, this was the easy one...)

SetViewportOrg() tells the DC which DEVICE coordinate maps to LOGICAL point (0,0). So calling SetViewportOrg() with half the width and height of your window (in pixels), for example, sets the LOGICAL point (0,0) to the center of the window.

SetWindowOrg() and SetViewportOrg() can be called on any Mapping Mode. They serve to offset the origin of one system within the other. They have no effect on the relative distances specified by the two coordinate systems.

Mapping Modes

There are several mapping modes, some of which are constrained to a fixed relationship between DEVICE and LOGICAL coordinate systems.
Here is the list:

Name/Constant          Constrained?           Logical Unit  
MM_TEXT (default)           Yes                             Pixel  
MM_LOENGLISH               Yes                            0.01 inch  
MM_HIENGLISH                Yes                            0.001 inch  
MM_LOMETRIC                Yes                            0.1 mm  
MM_HIMETRIC                 Yes                             0.01 mm  
MM_TWIPS                      Yes                             1/20 of a point, or 1/1440 of an inch
MM_ISOTROPIC              No                              User Defined  
MM_ANISOTROPIC         No                              User Defined

For the 'constrained' modes, all you are allowed is to change the origin of the logical system using either SetWindowOrg or SetViewportOrg.

This means that if I set the mapping mode to MM_LOENGLISH, the point (0,0) and (0,100) are 1 inch apart (1 Logical inch, defined by the device capabilities, see GetDeviceCaps())


The extent is the maximum value of an axis. Extents are a little trickier, because how they are interpreted depends on the mapping mode. They are only appropriate for the non-constrained modes, MM_ISOTROPIC and MM_ANISOTROPIC.

The difference between these two modes is that MM_ISOTROPIC takes the parameters you pass to SetViewportExt and SetWindowExt as 'suggestions' (see below) and adjusts the extents so the the x and y axis coordinates represent the same distance on the device. This way, a LOGICAL unit in the x direction is the same length (in terms of the output) as a LOGICAL unit in the y direction (this is not intuitive... some printers may have different resolutions in the two axes... this mode ensures that the LOGICAL units are equivalent in space).

MM_ANISOTROPIC differs in that the parameters passed to SetViewportExt and SetWindowExt are taken literally. Windows make no adjustment. Therefore, you can have very different coordinate systems in the two axes.

SetWindowExt and SetViewportExt are used as a team. These two functions set some internal members in the DC, which are used to map points between coordinate systems. As such, each is essentially meaningless taken alone.

SetWindowExt tells the DC that a rectangle with the LOGICAL width and height passed in, has the DEVICE width and height passed in via SetViewportExt. Confused yet?

Let's say that I call SetViewportExt for a display device with the parameters 100,50. Taken alone, that's rather meaningless. Now I call SetWindowExt with parameters 100,100. This means that for each LOGICAL unit in the x direction, I will move 1 DEVICE unit. On the other hand, for each LOGICAL unit I move in the y direction, I move 1/2 a unit in the DEVICE coordinate.

These functions can also be called with negative numbers. When the sign of the parameters to SetWindowExt and SetViewportExt a different, the direction of the axes changes. So that the positive y direction can be up, instead of the usual default of down, if I call:

One LOGICAL x unit translates to 1 DEVICE x unit, but 1 LOGICAL y unit translates to 1 DEVICE y unit, in the opposite direction.

Basically, here is the formula used (by Windows) to convert LOGICAL points to DEVICE points:

where xD = the DEVICE coordinate
and xL = the LOGICAL coordinate

xD = (xL - xWindowOrg)*(xViewportExt/xWindowExt) + xViewportOrg

If that makes anything clearer.
How Windows handles extents for MM_ISOTROPIC

Assuming that SetWindowExt is called BEFORE SetViewportExt (recommended), how the adjustments are made depends on the actual physical dimensions of the extents passed to SetViewportExt. Since device coordinates are not necessarily in equal units in the two axes, they are probably adjusted internally according to LOGPIXELSX/LOGPIXELSY.

If the physical dimensions of the Viewport Extents are wider than they are tall, the x extent is adjusted so that it's LOGICAL units are equal to the LOGICAL units for the y axis (as defined by the y parameters passed to SetWindowExt / SetViewportExt).

If the physical dimensions of the Viewport Extents are taller than they are wide, the y extent is adjusted.

No comments:

Post a Comment