MAPPING MODES Device Context coordinates: By default: "Device Units" used Video screen: pixels, from upper lefthand corner of screen Printer: "printer dots", from upper lefthand corner of page But pixels and dots not the same size Output looks different on different devices Better to use units like millimeters, etc. Mapping modes-- Allow app to use "logical units" for text and graphics Windows "maps" output to real device ==> Output will look similar on all devices Mapping Mode is a DC attribute setting Can be changed with (Win32API): SetMapMode(hDC, mapmode); Or (MFC): CDC::SetMapMode(mapmode) MAPPING MODE LOGICAL UNIT X-AXIS Y_AXIS ------------------------------------------------------ MM_TEXT Pixel(Default) Right Dn MM_HIENGLISH .001 inch Right Up MM_LOENGLISH .01 inch Right Up MM_HIMETRIC .01 mm Right Up MM_LOMETRIC .1 mm Right Up MM_TWIPS 1/20 point=1/1440 inch Right Up MM_ISOTROPIC Arbitrary (x==y) Selectable MM_ANISOTROPIC Arbitrary (x!=y) Selectable Windows maps logical coordinates to device coordinates Unit: pixel +x: toward right, +y" toward bottom Device coordinate systems: (1) Screen Coordinates-- Used by functions that don't write on client area e.g., CreateWindow(), MoveWindow() Origin is upper lefthand corner of the screen (2) Client-area Coordinates Used with DCs Origin is upper lefthand corner of window client area Logical coordinates mapped to these ClientToScreen() and ScreenToClient()-- Convert between device and client coordinate systems Text & Mapping Modes-- Changing mapping mode changes text location, but not size To change size, use a different font and select into DC Mapping Modes and Messages-- Device coordinates used for all messages not related to GDI functions e.g., WM_MOVE, WM_SIZE, WM_MOUSEMOVE Mapping mode only relevant with functions requiring a DC e.g., GDI drawing primitives GetSystemMetrics() and GetDeviceCaps() both use device units (TEXTMETRIC structure from GetTextMetrics() uses logical units) Viewport-- Rectangular area of screen specified in device coordinates Frequently the client area Window-- Rectangular area specified in logical coordinates Mapping-- Mathematical transformation that converts a window to a viewport Converts logical coordinates (xW,yW) to device coordinates (xV,yV) Done by Windows xV = (xVExt/xWExt) * (xW - xWOrg) + xVOrg yV = (yVExt/yWext) * (yW - yWOrg) + yVOrg Point (xW,yW) translated and scaled to (xV,yV) x scaling factor = (xVExt/xWext) y scaling factor = (yVExt/yWExt) These are determined by window/viewport "extents" Maintained by Windows Values can be gotten with (Win32API): GetWindowExtEx(hDC,&size) ; GetViewportExtEx(hDC,&size); Or (MFC): CDC::GetWindowExt( ); CDC::GetViewportExt( ); Both return a CSize object Values can be changed with (Win32 API): SetWindowExtEx(hDC,xWExt,yWExt,&orig_size); SetViewportExtEx(hDC,xVExt,yVExt,&orig_size); Or (MFC): CDC:: SetWindowExt(cx,cy); CDC:: SetViewportExt(cx,cy); (xWOrg,yWOrg) = window origin (xVOrg,yVOrg) = viewport origin (xWOrg,yWOrg) maps to (xVOrg,yVOrg) Both are (0,0) in default Device Context Moving the Origin-- Changing mapping mode may make output disappear! Because logical/physical axes point in the opposite direction Could use negative y values But better to move origin Can be done two ways: 1. Win32 API: SetWindowOrgEx(hDC,x,y,NULL); Or (MFC): CDC::SetWindowOrg(x,y); Moves upper lefthand corner of screen/paper up, right by x,y units Logical units used 2. Win32 API: SetVieportOrgEx(hDC,x,y,NULL); Or (MFC): CDC::SetViewportOrg(x,y); Moves lower lefthand corner of logical window down, right by x,y units Device units (pixels/dots) used Example-- Change mapping mode to MM_LOENGLISH Move logical origin to bottom left corner of client area Draw using those units CSize size; pDC = GetDC(); size = pDC->GetWindowExt (); // Returns x,y in a // CSize structure // Since in MM_TEXT // mode, "logical" // units are pixels pDC->SetViewportOrg (0,size.cy)); // Move the window down by size.cy pixels pDC->SetMapMode (MM_LOENGLISH); // Now we can draw using 0.01 inch logical // coordinates // Logical Origin is at lower lefthand // corner of client area MAPMODE1_MFC Example Application-- Demonstrates three mappings modes and moving of the origin Displays rectangle and line of text, using same logical coordinates 3 different mapping modes used Logical origin also drawn as a cross in each mapping mode Note different position/size of rectangle/text User clicks a mapping mode from menu WM_COMMAND processing: Sets nMapMode variable to appropriate value Forces repaint of client area calling Invalidate( ) Resulting WM_PAINT, OnDraw( ) processing: Calls SetMapMode( ) to set mapping mode according to value nMapMode Calls SetViewportOrg( ) to move window down/right by 100 pixels Calls MoveTo() and LineTo( ) to draw cross at logical origin Calls Rectangle( ) to draw a rectangle at log coords (50,50), (200,100) Calls TextOut( ) to display string at log coords (50,50) MM_ISOTROPIC, MM_ANISOTROPIC Mapping Modes-- Coordinate axes can use any unit size x-axis can point left or right the y-axis can point either up or down MM_ISOTROPIC--x and y units must be same size MM_ANISOTROPIC--Different sizes are used for x and y units Determine units to be used by setting X and Y scaling factors with calls to: SetWindowExtEx ( xWExt, yWExt); SetViewportExtEx ( xVExt, yVExt); Or: CDC:: SetWindowExt(xWExt, yWExt); CDC:: SetViewportExt(xVExt, yVExt); X scaling factor = xVExt/xWExt Y scaling factor = yVExt/yWExt Used in mapping equations To reverse either axis, set one Ext value to a negative number Example 1: Coordinate system where each logical unit is two pixels-- pDC = GetDC ( ); pDC->SetMapMode (MM_ISOTROPIC); pDC->SetWindowExt (1, 1); pDC->SetViewportExt (2, 2); // x-factor = y-factor = 2/1 Example 2--Logical coordinate system with y-axis up, y-unit = 1/4 pixel, x-axis is to be unchanged-- pDC = GetDC ( ); pDC->SetMapMode (MM_ANISOTROPIC); pDC->SetWindowExt (1, -4); pDC->SetViewportExt (1, 1); // x-factor=1/1) , y-factor=1/(-4) Example 3--Coordinate system where: client area always 1000 units high and wide y-axis points up CSize size; pDC = GetDC ( ); size=pDC->GetWindowExt (); // Get client area size Returns size in default MM_TEXT Mapping Mode units, here pixels pDC->SetMapMode (MM_ANISOTROPIC); pDC->SetWindowExt (1000, -1000); // x-factor = size.cx/1000 pDC->SetViewportExt (size.cx, size.cy); // y-factor = size.cy/(-1000) Mapping equations ==> x=1000 --> size.cx corresponds to right edge of the client area y=1000 --> -size.cy would correspond to upper of client area if y-axis were reversed Independent of how the window resized. MAPMODE2_MFC Example Application-- Draws rectangle and ellipse that always fit exactly inside client area User changes size of window (WM_SIZE message) ==> OnSize(cx,cy) --> cx,cy extent of window client area in device units Save in variables nX and nY Window exposed --> WM_PAINT message, OnDraw() ==> Set mapping mode to MM_ANISOTROPIC with pDC->SetMapMode() Set scaling factors to 100/nX & 100/nY with: pDC->SetViewportExtEx() using xWExt=nX and yWext=nY pDC->SetWindowExtEx() using xVExt=100 and yVExt=100 ==>logical coordinate system always scaled to 100 x 100 logical units Draw ellipse with pDC->Ellipse(0, 0, 100, 100) ==> After mapping, ellipse will lie inside rectangle (0,0)-(nX,nY) (just fits inside the client area) Draw rectangle with pDC->Rectangle(20, 20, 80, 80) ==> After mapping becomes (0.2*nX,0.2*nY)-(0.8*nX,0.8*nY) (borders 1/5 of way into the window) y-axis still points downward ==> don't have to move the origin