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.
Best Answer
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 returningUnitDisplay
. The documentation ofUnitDisplay
is: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.