24. Device Contexts III: Coordinate Systems

Page 407
  24. Device Contexts III: Coordinate Systems  
   
  The Windows GDI defines three spaces, each with its own coordinate system, for use with drawing functions. As shown in Figure 24-1, these spaces are world space, page space (also called logical space), and device space. A drawing function draws in either world space or page space, depending upon the setting of the so-called world transform. Once drawing is done, Windows applies one or more mapping functions, or transformations, indicated by the symbols T1, T2, and T3 in Figure 24-1, until the points reach the physical space of the device itself.  
 
  GDI Coordinate Systems  
   
  Note that the orientation of the axes in each space is the same as that of physical space. Note also that world space is supported only under Windows NT.  
   
  The mapping functions that Windows uses to map points from one space to the next can potentially be a composition of the following five basic transformations. However, as we will see, rotation and sheering are allowed only for the mapping T1 from world space to page space.  
   
  Translations  
   
  Reflections about an axis  
   
  Scaling (expansion or contraction along an axis)  
   
  Rotation about the origin  
   
  Sheering  
Page 408
   
  0408-01.gif  
   
  Figure 24-1.
Windows coordinate systems
 
   
  These transformations are pictured in Figure 24-2.  
   
  0408-02.gif  
   
  Figure 24-2.
The five basic transformations
 
   
  In case you are interested, in some sense, these transformations constitute all of the possible ''nice" transformations of the plane. For those who have studied linear algebra, we can say that any nonsingular linear transformation of the plane can be expressed as a composition of these transformations (excluding translation) and that any nonsingular affine transformation of the plane can be expressed as a composition of these transformations (including translation). (If you are interested in why this is so, let me suggest my book Introduction to Linear Algebra with Applications, published by Saunders College Publishing.)  
   
  The benefit of using these mappings is that the drawing functions can be much simpler, letting the mapping function take up much of the burden of drawing. For instance, to display an ellipse that is not centered at the origin, we can draw a circle centered at the origin in page space, let the transformations flatten it in the vertical direction into an ellipse, and then translate it to the desired location. We will do some examples to illustrate this soon.  
   
  Indeed, it is important to emphasize that drawing functions use either world or page coordinates, not physical coordinates. (We also cannot draw in device space.) The point is that the drawing functions themselves know nothing about  
Page 409
   
  the location of the origin of the physical device, nor about its units of measurement (pixels). This is as it should be.  
   
  For instance, the code:  
 
  Ellipse hDC, 1, 2, 5, 10  
   
  draws a filled ellipse whose bounding box has upper-left corner (1,2) and lower-right corner (5,10) in logical (or world) coordinates. There is no reference to the location of the physical origin, or to the orientation of the axes, or to inches, millimeters, pixels, or any other dimensions. All these are taken care of by the various mappings shown in Figure 24-1.  
   
  We mentioned that Windows does not allow all five types of transformations between each pair of spaces shown in Figure 24-1. The types of transformations that Windows allows between spaces are as follows:  
 
  Device space to physical space
T
3 can only be a translation.
 
 
  Page space to device space
T
2 can be a translation, followed by scaling (along one or both axes) and then a reflection (about one or both axes).
 
 
  World space to page space
T
1 can be any composition of all five basic transformations: translations, rotations, scalings, sheerings, and reflections.
 
 
  Virtual Space  
   
  There seems to be no compelling reason to think in terms of three separate nonphysical spaces, and it does seem to me that this viewpoint can lead to unnecessary complications. Another view we can take is that there is only one nonphysical space, which we will call virtual space, with its virtual coordinate system. As shown in Figure 24-3, all drawing is done in virtual space, using virtual coordinates. Quite simply, we draw in virtual space, using virtual coordinates, and Windows applies a single mapping function T, which is the composition of the three mappings in Figure 24-1, to produce the final display.  
   
  On the other hand, since Microsoft has seen fit to define the three spaces shown in Figure 24-1, we will discuss them along with our virtual space. Then you can decide how you prefer to view the process.  
 
  Device Space  
   
  Device space is special in several ways. First, we cannot draw in device space the Windows GDI provides no such drawing functions. Second, Windows permits  
Page 410
   
  0410-01.gif  
   
  Figure 24-3.
Virtual space
 
   
  only a translation of points in device space to obtain the corresponding points in physical space it does not permit any of the other basic transformations. To specify the translation between device space and physical space, we use the SetViewportOrgEx function:  
  BOOL SetViewportOrgEx(       HDC hdc,             // handle of device context       int ViewportOriginX, // x-coordinate of viewport origin in pixels       int ViewportOriginY, // Y-coordinate of viewport origin in pixels       LPPOINT lpPoint      // address of structure receiving original origin     );
   
  (Note that lpPoint may be set to 0, in which case it is ignored.) The point:  
 
  (ViewportOriginX,ViewportOriginY)  
   
  defines the viewport origin, in device units (pixels). This is the point in physical space that the origin of device space is translated to, as shown in Figure 24-4.  
   
  0410-02.gif  
   
  Figure 24-4.
Device space to physical space
 
   
  Thus, the formula for the transformation T3 in Figure 24-1 is:  
 
  PhysicalX = DeviceX + ViewportOriginX
PhysicalY = DeviceY + ViewportOriginY
 
Page 411
   
  Note that Windows defines the functions LPToDP and DPToLP to see the effect of the translation between the two spaces.  
   
  It is difficult to isolate this translation in order to see it in action directly, because we cannot draw in device space. However, we will see its effect in a later example.  
 
  Page Space  
   
  As we have said, the mapping T2 in Figure 24-1, from page space to device space, is a translation, followed by scaling (along one or both axes) and then a reflection (about one or both axes). (Any of these transformations can be the identity, of course.)  
   
  The Translation  
   
  We define the translation using the function SetWindowOrgEx (a rather unfortunate name):  
  BOOL SetWindowOrgEx(       HDC hdc,             // handle of device context       int WindowOrginX,    // x-coordinate of window origin in logical units       int WindowOrginY,    // y-coordinate of window origin in logical units       LPPOINT lpPoint      // address of structure receiving original origin     );
   
  This function defines the window origin, which is the point that is mapped to the origin by the translation. (Note the difference here: the window origin is mapped to the logical origin, whereas the viewport origin is mapped from the device origin.)  
   
  The formula for this translation is:  
 
  NewX = LogicalX - WindowOriginX
NewY = LogicalY - WindowOriginY
 
   
  Figure 24-5 shows this initial translation.  
   
  The Scaling  
   
  The next step in the transformation from page space to device space involves a scaling in the direction of one or both of the logical axes. To scale in the x-direction, we multiply the x-coordinate of a point by a positive number. If the number is less than 1, the scaling is a contraction; if it is greater than 1, the scaling is an expansion.  
   
  Often, a scale factor is a noninteger. But Windows works more efficiently with integers (VB longs). Accordingly, in Windows, scale factors are set by setting two values, called extents, in each direction. These are pictured in Figure 24-5. The scale factor is taken to be the ratio of the extents for the given direction.  
Page 412
   
  0412-01.gif  
   
  Figure 24-5.
Page space to device space
 
   
  Thus, in the horizontal direction, we have:  
   
  0412-02.gif  
   
  The ViewportExtentX value is given in pixels, and the WindowExtentX value is in logical units. Hence, the scale factor ScaleX is in pixels per logical unit. In other words, setting the scale factor sets the number of pixels per logical drawing unit. (In a way, this is not really a scaling, because page space does not exist physically. It is simply a setting of units.) For instance, to set a scale factor of:  
 
  1 logical unit = 1/64 monitor inch  
   
  we would set (assuming a large system font and hence 120 pixels per monitor inch):  
 
  WindowExtentX = 64
ViewportExtentX = 120     ' 1 monitor inch
 
   
  In this way, we can refer in the GDI drawing functions to 1 logical unit, which equals 1/64 of a monitor inch. Thus, another way to look at scaling is that it sets the precision or resolution of the drawing, that is, the smallest physical measurement to which we can refer.  
   
  Similarly, to perform the vertical scaling, Windows multiplies by the ratio:  
   
  0412-03.gif  
 
Page 413
   
  Thus, a single logical unit on the vertical axis corresponds to ScaleY pixels on the physical device.  
   
  We can now combine the effects of the translation and the scaling by writing:  
   
  0413-01.gif  
   
  To set the extents, we use the GDI functions:  
 
  BOOL SetWindowExtEx(
  HDC hdc,       // handle of device context
  int nXExtent,  // new horizontal window extent in logical units
  int nYExtent,  // new vertical window extent in logical units
  LPSIZE lpSize  // original window extent
);

BOOL SetViewportExtEx(
  HDC hdc,       // handle of device context
  int nXExtent,  // horizontal viewport extent in pixels
  int nYExtent,  // vertical viewport extent in pixels
  LPSIZE lpSize  // original viewport extent
);
 
Note that lpSize can be set to 0, in which case it is ignored.  
   
  The Reflection  
   
  To perform a reflection about the x-axis, for instance, we need to multiple the y-coordinate (yes, the y-coordinate) of the point by -1. Similarly, to reflect about the y-axis, we multiply the x-coordinate by -1.  
   
  Rather than specify separately in some parameter or other that reflection is requested, Windows allows us simply to change the sign of either one of the extents. It doesn't matter whether we change the window or the viewport extent the effect on the ratio is the same. Thus, the formulas:  
   
  0413-02.gif  
   
  describe the entire transformation from page space to device space.  
 
  Virtual Space to Physical Space  
   
  The transformation from world coordinates to logical coordinates provides rotation and sheering, which are not allowed in the transformation from page space to  

4th Edition

Page 414
   
  device space. However, these transformations are used much less often than the other three basic transformations. Since world space is supported only in Windows NT, we will discuss it only briefly in the "World Space" section later in the chapter.  
   
  When world coordinates are not involved, and we think in terms of virtual space, the transformation from virtual to physical space is given by the formulas:  
   
  0414-01.gif  
   
  As we will discuss soon, Windows supports several mapping modes, of which only one the anisotropic mode allows the freedom to set all of the values in these formulas. In the other modes, Windows helps us out by setting some of these values for us, making the formulas simpler to use.  
   
  Example  
   
  Suppose we want to draw the ellipses shown on the right in Figure 24-6. The measurements on the ellipses are in monitor inches.  
   
  0414-02.gif  
   
  Figure 24-6.
Drawing ellipses
 
   
  There are many ways to proceed here. One approach is to draw the circles shown on the left in Figure 24-6 and then map these circles into the desired ellipses by:  
Page 415
   
  Scaling the y-direction by a factor of 1/2  
   
  Reflecting about the x-axis  
   
  Translating the logical origin to the physical point (1,1)  
   
  First, we take care of some preliminaries saving the device context of the picture box, setting the pen and brush types, getting the number of pixels per inch for the monitor, and setting the mapping mode to anisotropic (more on this later in "Mapping Modes"):  
 
  ' Save DC for later restoration
hDCPic = SaveDC(pic.hdc)

' Set pen width
SelectObject pic.hdc, GetStockObject(BLACK_PEN)

' Set brush
SelectObject pic.hdc, GetStockObject(NULL_BRUSH)

' Get size of monitor inch
PixPerInchX = GetDeviceCaps(pic.hdc, LOGPIXELSX)
PixPerInchY = GetDeviceCaps(pic.hdc, LOGPIXELSY)

' Set mapping mode
SetMapMode pic.hdc, MM_ANISOTROPIC
 
   
  I find it simpler to proceed as though the parameters (extents and origins) can be nonintegral values and then make the necessary adjustments afterward.  
   
  Thus, we set the scale factor to 1 logical unit per inch horizontally and 2 logical units per inch vertically. These scale factors will cause a logical circle to contract in the y-direction by a factor of 2, thus producing the desired elliptical shapes. At the same time, we reflect about the x-axis by setting the viewport vertical extent to a negative number:  
 
  SetWindowExtEx pic.hdc, 1, 2, sz
SetViewportExtEx pic.hdc, PixPerInchX, -PixPerInchY, sz
 
   
  The translation is done by setting the viewport origin to (1,1) in monitor inches:  
 
  SetViewportOrgEx pic.hdc, PixPerInchX, PixPerInchY, pt  
   
  Now we can draw the circles, using the bounding squares shown in Figure 24-6:  
 
  Ellipse pic.hdc, -1, -1, 1, 1
Ellipse pic.hdc, -0.5, 1, 0.5, 2
 
   
  Finally, we can deal with the problem that the parameters must be longs. To fix this, we need to multiply the coordinates of the bounding boxes by 2. To compensate, we need to do the same for the logical units. Here is the correct code:  
 
  ' Set scale factors and flip
SetWindowExtEx pic.hdc, 2, 4, sz
 
 

Page 416
 
  ' Set the translation
SetViewportOrgEx pic.hdc, PixPerInchX, PixPerInchY, pt

' Draw the logical ellipses (circles)
Ellipse pic.hdc, -2, -2, 2, 2
Ellipse pic.hdc, -1, 2, 1, 4
 
   
  The output is shown in Figure 24-7.  
   
  0416-01.gif  
   
  Figure 24-7.
The output
 
 
  Setting Up Logical Coordinates in Physical Space  
   
  Another way to look at the virtual space to physical space mapping is to look at the image of the logical coordinate axes under the mapping. The image lies in physical space, of course. To view the effect of scaling, we treat the logical axes as line segments of a fixed length. (Axes are actually lines, not line segments.)  
   
  Figure 24-8 shows the image of the logical coordinates under the previous transformation.  
   
  The virtue of this viewpoint is that we can think of the transformation as defining a new coordinate system in physical space. Some authors like to refer to this new coordinate system as a logical coordinate system, which is not strictly speaking correct. This coordinate system is not in page space it is in physical space. Nonetheless, we will also follow this convention.  
   
  Example  
   
  To illustrate this point of view, let us consider another example. Suppose we want to create the drawing in the picture box in Figure 24-9.  
Page 417
   
  0417-01.gif  
   
  Figure 24-8.
The image of the logical axes
 
   
  0417-02.gif  
   
  Figure 24-9.
An example of GDI drawing
 
   
  Here are the details of this drawing:  
   
  There are 4 radial lines of length 1 monitor inch emanating from the physical origin of the picture box. The lines split the quadrant into equal-sized sectors.  
   
  There are 5 ellipses centered in the picture box. The inside ellipse has a major (horizontal) axis of length 1/4 monitor inch. Each successive major axis is an additional 1/4 inch long. The minor axis of each ellipse is one-half as long as its major axis.  
Page 418
   
  To make this drawing, we use one logical coordinate system for the lines and another for the ellipses, as shown in Figure 24-10.  
   
  0418-01.gif  
   
  Figure 24-10.
Setting up logical coordinates in physical space
 
   
  As before, the code begins with some preliminaries:  
 
  ' Save DC for later restoration
hDCPic = SaveDC(pic.hdc)

' Set pen type
SelectObject pic.hdc, GetStockObject(BLACK_PEN)

' Set brush
SelectObject pic.hdc, GetStockObject(NULL_BRUSH)

' Get size of monitor inch
PixPerInchX = GetDeviceCaps(pic.hdc, LOGPIXELSX)
PixPerInchY = GetDeviceCaps(pic.hdc, LOGPIXELSY)

' Set mapping mode
SetMapMode pic.hdc, MM_ANISOTROPIC
 
   
  Next, we set up the mapping for drawing the rays. We set the scale factor at 100 logical units per monitor inch, which should provide sufficient resolution for the endpoints of the line segments, which are rounded to longs by Windows:  
 
  ' ---------
' Draw Rays
' ---------

' No translations
SetWindowOrgEx pic.hdc, 0, 0, pt
SetViewportOrgEx pic.hdc, 0, 0, pt
 
 

Page 419
 
  ' Scale factor: 100 logical unit per monitor inch
SetWindowExtEx pic.hdc, 100, 100, sz
SetViewportExtEx pic.hdc, PixPerInchX, PixPerInchY, sz

' Draw lines
For i = 1 to 4
   ' Move current point to logical origin
   MoveToEx pic.hdc, 0, 0, pt
   ' Draw ray of radius 100 logical units
   LineTo pic.hdc, 100 * Cos(1.57 * i / 5), 100 * Sin(1.57 * i / 5)
Next
 
   
  To draw the ellipses, we change the logical coordinate system. Note that we flip about the x-axis, even though this is not really necessary, since the ellipses are symmetric about this axis.  
 
  ' No initial translation
SetWindowOrgEx pic.hdc, 0, 0, pt

' Final Translation from (0,0) logical to midpoint of picture box
SetViewportOrgEx pic.hdc, (pic.Width / 2) / Screen.TwipsPerPixelX, _
   (pic:Height / 2) / Screen.TwipsPerPixelY, pt

' Scale factor set to 8 logical units per monitor inch horizontally
' Set vertical scale to flatten ellipses by 2 to 1
' Reflect about x-axis (this is not really necessary)
SetWindowExtEx pic.hdc, 8, 16, sz
SetViewportExtEx pic.hdc, PixPerInchX, -PixPerInchY, sz

' Draw ellipses
For i = 1 To 5
   Ellipse pic.hdc, -i, -i, i, i
Next i

' Restore DC
RestoreDC pic.hdc, hDCPic
 
 
  Mapping Modes  
   
  In the previous examples, we were able to set the origins and extents to any values. This was permitted by setting the mapping mode to MM_ANISOTROPIC:  
 
  SetMapMode pic.hdc, MM_ANISOTROPIC  
   
  However, in many cases, we do not need this much freedom. For instance, we may want to scale the two axes proportionally, to preserve the aspect ratio (ratio of unit lengths on the two axes). This will guarantee that a circle or square drawn in page space will be mapped to a circle or square in physical space.  
   
  To accommodate a variety of common situations, Windows defines several mapping modes. These determine who gets to set the various origins and extents Windows or the programmer.  
Page 420
   
  As we have seen, the mapping mode is set using the SetMapMode function:  
 
  int SetMapMode(
   HDC hdc,           // handle of device context
   int fnMapMode      // new mapping mode
);
 
   
  where fnMapMode can be set to one of the mapping mode constants: MM_ANISOTROPIC, MM_ISOTROPIC, MM_TEXT, MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC. MM_LOMETRIC, or MM_TWIPS. These mapping modes are described in the following sections.  
   
  Text Mapping Mode  
   
  In text-mapping mode, the extents are set to (1,1) by Windows and cannot be changed. Thus, no scaling is performed and each logical unit corresponds to a single physical unit (pixel). The programmer can set the origins to permit custom translations. The default origins are (0,0). When these defaults are used, page space and physical space are essentially the same.  
   
  The Metric Mapping Modes  
   
  The modes MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS are similar. In each case, the origins can be set by the programmer, and have default values (0,0). The extents are set by Windows so that each logical unit corresponds to a certain physical dimension. This saves us the trouble of figuring out how many device units (pixels or printer dots) make up an inch, for instance.  
   
  Here are scale factors. Note that the prefix HI refers to high precision, whereas LO refers to low precision.  
 
  MM_HIENGLISH
Each logical unit corresponds to 0.001 of an inch
 
 
  MM_HIMETRIC
Each logical unit corresponds to 0.01 of a millimeter
 
 
  MM_LOENGLISH
Each logical unit corresponds to 0.01 of an inch
 
 
  MM_LOMETRIC
Each logical unit corresponds to 0.1 of a millimeter
 
 
  MM_TWIPS
Each logical unit corresponds to one-twentieth of a printer's point (approximately 1/1440 inch)
 
   
  Finally, each of these mapping modes also involves a reflection about the x-axis. Hence, in order to draw in visible physical space, we must draw using negative values of y. For instance, the results of the code:  
Page 421
 
  ' Set origins
SetWindowOrgEx pic.hdc, 0, 0, pt
SetViewportOrgEx pic.hdc, 0, 0, pt

' Set mapping mode
SetMapMode pic.hdc, MM_LOENGLISH

' Move to logical origin
MoveToEx pic.hdc, 0, 0, pt

' Draw line
LineTo pic.hdc, 100, -100

' Draw text
TextOut pic.hdc, 100, -100, "test", 4

' Draw rectangle
Rectangle pic.hdc, 50, -50, 10, -10
 
   
  are shown in Figure 24-11.  
   
  0421-01.gif  
   
  Figure 24-11.
MM_LOENGLISH mode
 
   
  Note finally that to achieve the scale factors described above for the English and metric mapping modes, Windows sets the window and viewport extents for us. Although we will not go into the details of how this is done, it is interesting to note that the procedure is different under Windows 9x than under Windows NT.  
   
  Anisotropic Mapping Mode  
   
  In this mode, as we have seen, the programmer is free to set the window and viewport origins and extents. Hence, this mode gives the most freedom of choice to the programmer.  
Page 422
   
  Isotropic Mapping Mode  
   
  Isotropic mode is like anisotropic mode except in one respect. Windows adjusts the extents so that the logical units on each axis represent the same distance on the physical device. If the pixels are square (and only if the pixels are square), this can be accomplished by assuring that a logical unit is mapped to the same number of pixels in each direction. Windows will shrink the necessary viewport extent to accomplish this goal.  
   
  Of course, the purpose of isotropic mode is to ensure that aspect ratios are preserved, so that, for example, a logical square will display as a square on the physical device and a logical circle will display as a circle.  
   
  We can observe for ourselves how Windows changes the viewport extents with some simple code that uses the GetViewportExtents function:  
 
  ' Set mapping mode
SetMapMode pic.hdc, MM_ISOTROPIC

SetWindowOrgEx pic.hdc, 0, 0, pt
SetViewportOrgEx pic.hdc, 0, 0, pt

' 1000 logical units for both width and height of picture box
SetWindowExtEx pic.hdc, 1000, 1000, sz
SetViewportExtEx pic.hdc, pic.Width / Screen.TwipsPerPixelX, _
   pic.Height   Screen.TwipsPerPixelY, sz

' Draw rectangle
Rectangle pic.hdc, 100, 100, 900, 900

Debug.Print "Viewport Extents Setting: " & pic.Width / Screen.TwipsPerPixelX _
   & " / " & pic.Height / Screen.TwipsPerPixelY
GetViewportExtEx pic.hdc, sz
Debug.Print "Viewport Extents: " & sz.cx & " / " & sz.cy
 
   
  Running this code shows that Windows adjusts the vertical viewport extent, because the picture box is taller than it is wide:  
 
  Viewport Extents Setting: 176 / 391
Viewport Extents: 176 / 176
 
   
  Note that when using this mode, it is important to call SetWindowExtEx before setting SetViewportExtEx.  
 
  World Space  
   
  Since world space is supported only by Windows NT, we will discuss it only briefly.  
Page 423
   
  The transformation from world space to page space can be a composition of any of the five basic transformations: translation, rotation, reflection, scaling, and sheering. Thus, it is the most general transformation possible and also duplicates some of the functionality of the other transformations.  
   
  The world space to page space transformation is set using the SetWorldTransform function:  
 
  BOOL SetWorldTransform(
  HDC hdc,             // handle of device context
  CONST XFORM *lpXform  // address of transformation data
);
 
   
  Here XFORM is a structure:  
 
  struct _XFORM {
    FLOAT eM11;
    FLOAT eM12;
    FLOAT eM21;
    FLOAT eM22;
    FLOAT eDx;
    FLOAT eDy;
}
 
   
  that defines the transformation. (Despite the documentation, a world transformation is not a linear transformation unless the translation is 0.) The point (x,y) is mapped to the point (x',y') using the following formulas:  
 
  x' = x * eM11 + y * eM21 + eDx
y' = x * eM12 + y * eM22 + eDy
 
   
  In matrix language, this can be written as follows:  
   
  0423-01.gif  
   
  Let us write this formula in the form:  
   
  0423-02.gif  
   
  where:  
   
  0423-03.gif  
   
  The values of eDx and eDy are just the amounts to translate in the x- and y- directions, respectively. As to the matrix M, it can be built using matrix multiplication. First, we need to decide the order in which we want to apply the rotation, reflection, scaling, and sheering. Then we create a matrix to do each of these jobs and multiply these matrices together to get M.  
   
  Here are the specifics.  
Page 424
   
  Rotation  
   
  To rotate in the direction from positive x-axis towards positive y-axis through the angle A, use the matrix:  
   
  0424-01.gif  
   
  Reflection  
   
  To reflect about the x-axis or y-axis, use the matrices FX and FY, respectively:  
   
  0424-02.gif  
   
  Scaling  
   
  To scale in the x-direction or y-direction by an amount r>0, use the matrices SX(r) and SY(r), respectively:  
   
  0424-03.gif  
   
  Sheers  
   
  To sheer in the x-direction or y-direction by an amount r, use the matrices HX(r) and HY(r), respectively:  
   
  0424-04.gif  
   
  To illustrate, the following code produces the output in Figure 24-12.  
 
  Public Sub RotatingText ()

' Save DC for later restoration
hDCPic = SaveDC(pic.hdc)

' Set pen width
SelectObject pic.hdc, GetStockObject(BLACK_PEN)

' Set brush
SelectObject pic.hdc, GetStockObject(NULL_BRUSH)

' Get size of monitor inch
PixPerInchX = GetDeviceCaps(pic.hdc, LOGPIXELSX)
PixPerInchY = GetDeviceCaps(pic.hdc, LOGPIXELSY)
 
 

Page 425
 
  ' Set mapping mode
SetMapMode pic.hdc, MM_ANISOTROPIC
SetGraphicsMode pic.hdc, GM_ADVANCED

' Set scale for logical to physical
SetWindowExtEx pic.hdc, 80, 80, sz
SetViewportExtEx pic.hdc, PixPerInchX, PixPerInchY, sz

' Start in middle of device
SetViewportOrgEx pic.hdc, pic.Width / 2  / Screen.TwipsPerPixelX, _
     pic.Height / 2 / Screen.TwipsPerPixelY, pt

' Draw ellipse
Ellipse pic.hdc, -18, -18, 18, 18
Const pi = 3.14159

For i = 0 To 15

   xf.eDx = 0
   xf.eDy = 0
   xf.eM11 = Cos(i * pi / 8)
   xf.eM12 = Sin(i * pi / 8)
   xf.eM21 = -xf.eM12
   xf.eM22 = xf.eM11
   SetWorldTransform pic.hdc, xf
   TextOut pic.hdc, 0, 0, "     rotating text", 18

Next

End Sub
 
   
  0425-01.gif  
   
  Figure 24-12.
Rotating text using world space
 
   
  Finally, you will be happy to hear that the Windows GDI supplies some functions to help with matrix multiplication: CombineTransform and ModifyWorldTransform.  


WIN32 API Programming with Visual Basic
Win32 API Programming with Visual Basic
ISBN: 1565926315
EAN: 2147483647
Year: 1999
Pages: 31
Authors: Steven Roman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net