GDI Mapping Modes Explained

Date: 8 May 1997
Author: Rich Goldstein, MD  (goldstei at interport dot net)

Overview

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.

So WHAT ARE THEY, ALREADY???

Origins

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.

Advanced Note:
  Believe it or not, there are some (though few) reasons to use BOTH SetWindowOrg() and SetViewportOrg(). OWL's TScroller provides one such opportunity. In AutoOrg mode, the TScroller calls SetViewportOrg in it's BeginView member function, which is called before Paint is called. If your LOGICAL coordinate system uses an origin other that the top-left of the window, you can call SetWindowOrg in the Paint method to choose some other origin. Because both functions can be used safely together, you can let TScroller adjust the Viewport origin to facilitate scrolling, and you can adjust the Window origin to facilitate alternative 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())

Extents

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:

SetWindowExt(1,-1);
SetViewportExt(1,1);

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.


Contents of this page Copyright © 1997, Rich Goldstein, MD. All Rights Reserved.