windows 关于 GDI/GDI+ 坐标兼容性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2309750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
About GDI/GDI+ coordinate compatibility?
提问by Yngve Hammersland
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
?
我在交替使用 GDI 和 GDI+ 绘图时遇到问题。页面转换——尤其是缩放——似乎在两者之间有点偏差。除了SetViewportExt
和之外,GDI 上下文的哪些属性会影响输出的缩放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.
该代码几乎完全使用 GDI 进行绘图,但在需要其功能(半透明)的少数情况下使用 GDI+。它使用SetViewportExt
,SetWindowExt
和SetViewportOrg
来启用缩放和滚动。
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).
当需要 GDI+ 时,我会Gdiplus::Graphics
在 HDC 周围构建一个对象并进行绘图。我假设这会使图形上下文包装设备上下文并将其渲染中继到设备上下文。如果我提取 GDI+ 图形上下文的转换矩阵,我会看到它是单位矩阵,因此缩放是在其他地方完成的(我猜是在设备上下文中)。
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:
我设计了一个简单的测试,我使用 GDI 和 GDI+ 绘制相同的矩形阵列,以确保所有转换在两种情况下都相同。代码片段如下:
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+)
结果在这里:(蓝色虚线由GDI绘制,灰色由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.
我可以清楚地看到两个坐标系是不同的。我预计会出现一些舍入误差,但不会出现此处所见的缩放误差。此外,当我更改缩放系数时,GDI+ 会根据缩放在两个方向上跳跃 ±4 像素左右。屏幕截图中也突出显示了这一点,因为与 GDI 矩形相比,GDI+ 矩形在 X 轴上具有正偏移量,在 Y 轴上具有负偏移量。
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.
有人知道这里发生了什么吗?
我将如何调查/调试这个?这发生在 Windows 内部,所以很遗憾我无法调试它。
For reference, this is what my viewport/window org/ext looks like:
作为参考,这就是我的视口/窗口 org/ext 的样子:
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.
在屏幕空间中取两个坐标(原点和第二个合适的点),并使用 GDI(
DPtoLP
函数)将它们转换为逻辑坐标。将 GDI 转换重置为
MM_TEXT
.使用变换后的点为 GDI+ 构造一个变换矩阵,表示相同的变换
最后使用这个矩阵来构造一个具有正确转换的 GDI+ 上下文。
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 possibleto have a GDI+ context mimic the GDI transformation.
这有点黑客,但它有效。不过,我仍然不知道为什么两者之间存在差异。至少它表明可以使用 GDI+ 上下文来模拟 GDI 转换。
回答by myavuzselim
Short answer: call graphics.SetPageUnit(Gdiplus::UnitPixel)
简答:打电话 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).
我遇到了与https://stackoverflow.com/a/4894969/700027相同的问题:打印时,GDI+ (Gdiplus::Graphics) 的坐标与 GDI (HDC) 的坐标不匹配。
graphics.GetPageUnit()
was returning UnitDisplay
. The documentation of UnitDisplay
is:
graphics.GetPageUnit()
正在返回UnitDisplay
。的文档UnitDisplay
是:
Specifies display units. For example, if the display device is a monitor, then the unit is 1 pixel.
指定显示单位。例如,如果显示设备是显示器,则单位为 1 个像素。
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.
我错误地认为对于打印机,UnitDisplay
会使用打印机点。经过一番挣扎,我终于发现它实际上出于未知原因使用了 1/100 英寸。如果我使用 Gdiplus::UnitPixel,则 GDI+ 坐标与 GDI 坐标相同。
回答by Tim Cooper
We have had the same problem.
我们遇到了同样的问题。
(Background: GDI works fine for just about everything, and seems to be much faster for our spreadsheet-style displays with 1000's of cells of text that need to be rendered. However we need GDI+ for displaying .jpg's.)
(背景:GDI 几乎适用于所有情况,对于需要呈现 1000 多个文本单元格的电子表格样式显示似乎要快得多。但是我们需要 GDI+ 来显示 .jpg 的。)
GDI+ scaling seemed correct when displaying stuff on the screen. We have a Print Preview feature which uses a coordinate transformation to let the app render using printer coordinates but to appear on the screen. Everything worked fine until we sent it to an actual printer (or PDF writer) when the scaling got stuffed up.
在屏幕上显示内容时,GDI+ 缩放似乎是正确的。我们有一个打印预览功能,它使用坐标变换让应用程序使用打印机坐标进行渲染但显示在屏幕上。一切正常,直到我们将其发送到实际的打印机(或 PDF 编写器)时,缩放比例变大了。
After a man-week's worth of work (and getting a hint from you with your solution), this is our understanding:
经过一个人周的工作(并从您那里得到解决方案的提示),这是我们的理解:
There is a bug in GDI+ that when you call: "Graphics(HDC)" (create a Graphics object from a GDI device context), where the HDC is from a printer or software printer with e.g. 6000 x 4000 pixel resolution, then GDI+ ignores the fact that the HDC is working with this large resolution and instead it applies its own resolution of about 1000 x 800 pixels.
GDI+ 中有一个错误,当您调用:“Graphics(HDC)”(从 GDI 设备上下文创建图形对象),其中 HDC 来自打印机或软件打印机,例如 6000 x 4000 像素分辨率,然后 GDI+ 忽略事实上,HDC 正在使用这种大分辨率,而是应用自己的大约 1000 x 800 像素的分辨率。
Therefore, your workaround is probably the correct and best solution to the problem.
因此,您的解决方法可能是问题的正确和最佳解决方案。
Our solution is similar but a little different, on the grounds that we don't actually want any coordinate transforms:
我们的解决方案类似但略有不同,理由是我们实际上并不想要任何坐标变换:
graphics.GetVisibleClipBounds(&rect);
double deltaY = (double)GetPrinterH()/(double)rect.Height;
double deltaX = (double)GetPrinterW()/(double)rect.Width;
x1=x1/deltaX;
x2=x2/deltaX;
y1=y1/deltaY;
y2=y2/deltaY;
graphics.DrawImage(jpeg->image, x1,y1,x2-x1,y2-y1);
These scaling factors seem to be very close to '6' on many printer drivers.
在许多打印机驱动程序上,这些比例因子似乎非常接近“6”。
回答by DOS
I found a workaround for the issues when printing. Note that the graphics object in the example doesn't set any world space transformations, so drawing is done directly in page space.
我在打印时找到了解决问题的方法。请注意,示例中的图形对象没有设置任何世界空间转换,因此直接在页面空间中进行绘制。
Setting the page units to inches, then converting coordinates to inches seems to fix the drawing issues without much additional work. Tested with display and printer DCs at different DPIs (ranging from 72 to 4000.)
将页面单位设置为英寸,然后将坐标转换为英寸似乎无需太多额外工作即可解决绘图问题。使用不同 DPI(范围从 72 到 4000)的显示器和打印机 DC 进行测试。
Gdiplus::Graphics graphics(..);
Gdiplus::RectF rect(0.0f, 0.0f, 1.0f, 1.0f);
Gdiplus::REAL dpiX = graphics.getDpiX();
Gdiplus::REAL dpiY = graphics.getDpiY();
/* Logical coordinates to inches. In this example, the window extents are
equal to the DC's DPI. You will have to convert to inches based on your
specific configuration. */
rect.X /= dpiX;
rect.Y /= dpiY;
rect.Width /= dpiX;
rect.Height /= dpiY;
graphics.SetPageUnit(Gdiplus::UnitInch);
graphics.FillRectangle(.., rect);
回答by rep_movsd
One thing to remember is that most of GDI usually runs on the hardware (i.e. GDI functions map to the display driver that implement some functionality on silicon) GDI+ was supposed to get hardware acceleration, but it remained as a software alone renderer.
需要记住的一件事是,大多数 GDI 通常在硬件上运行(即 GDI 功能映射到在硅片上实现某些功能的显示驱动程序) GDI+ 应该获得硬件加速,但它仍然是一个单独的软件渲染器。
Try manually setting a few pixels via GDI+ and GDI and see if they differ.
尝试通过 GDI+ 和 GDI 手动设置几个像素,看看它们是否不同。
Perhaps the way your particular graphics card transforms co-ordinates deviates from the way it happens in GDI+
也许您的特定显卡转换坐标的方式与 GDI+ 中发生的方式不同