About GDI/GDI+ coordinate compatibility?
c++, gdi, gdi+, windows
Solution
Short answer: call `graphics.SetPageUnit(Gdiplus::UnitPixel)`
I had the same problem as https://stackoverflow.com/a/4894969/700027: when printing, the coordinates for GDI+ (Gdiplus::Graphics) did not match the coordinates from GDI (HDC).
`graphics.GetPageUnit()` was returning `UnitDisplay`. The documentation of `UnitDisplay` is:
Specifies display units. For example, if the display device is a monitor, then the unit is 1 pixel.
I had wrongly assumed that for a printer, `UnitDisplay` would use printer dots. After much struggle I finally found out that it was actually using 1/100 of inches for an unknown reason. If I use Gdiplus::UnitPixel then GDI+ coordinates are the same as GDI coordinates.
Problem
I have a problem while drawing with both GDI and GDI+ interchangeably. The page transformation—in particular scaling—seems to be a little bit off between the two. Which properties of the GDI context affects the scaling of the output other than `SetViewportExt` and `SetWindowExt`? The code uses almost exclusively GDI for its drawing, but uses GDI+ in a few cases where its features (semitransparency) are needed. It uses `SetViewportExt`, `SetWindowExt` and `SetViewportOrg` to enable zooming and scrolling. When GDI+ is needed I construct a `Gdiplus::Graphics` object around the HDC and do the drawing. I assume this makes the graphics context wrap the device context and relay its rendering to the device context. If I extract the transformation matrix of the GDI+ graphics context, I see that it is the identity matrix, so the scaling is done elsewhere (in the device context I guess). I devised a simple test where I draw the same array of rectangles with GDI and GDI+ to be sure that all the transformations are the same in both cases. The code snippet follows: ``` CRect rect = ...; // Draw the rectangle using GDI CPen cpen(PS_DASH, 0, RGB(0,0,255)); pDC->SelectObject(&cpen); pDC->Rectangle(rect); { // Draw the rectangle using GDI+ Gdiplus::Graphics graphics(pDC->m_hDC); Gdiplus::Pen pen(Gdiplus::Color(180,180,180)); graphics.DrawRectangle( &pen, Gdiplus::Rect(rect.left, rect.top, rect.Width(), rect.Height())); } ``` And the result is here: (the blue dashed is drawn by GDI and the gray is drawn by GDI+) I can clearly see that the two coordinate systems are different. I expected some round-off errors, but not a scaling error as seen here. Also, when I change the zoom factor, the GDI+ jumps around ±4 pixel in both directions depending on the zoom. This is also highlighted in the screenshot as the GDI+ rectangle has a positive offset on the X axis and a negative offset on the Y axis as compared to the GDI rectangle. Does anybody know what's going on here? How would I go about investigating/debugging this? This happens in the bowels of windows so I'm unfortunately unable to debug it. For reference, this is what my viewport/window org/ext looks like: ``` Window Ext: (134000, 80500) Window Org: (0, 0) Viewport Ext: (1452 872) Viewport Org: (35 35) ``` Update: I have fixed the problem, but it's not pretty. The basic approach is: Take two coordinates (origin and a second appropriate point) in screen space and transform them to logical coordinates using GDI (`DPtoLP` function). Reset the GDI transformation to `MM_TEXT`. Use the transformed points to construct a transformation matrix for GDI+ which represent the same transformation And finally use this matrix to construct a GDI+ context with the correct transformation. This is a bit of a hack, but it works. I still don't know why there is a difference between the two, though. At least it goes to show that it is possible to have a GDI+ context mimic the GDI transformation.