【问题标题】:About GDI/GDI+ coordinate compatibility?关于 GDI/GDI+ 坐标兼容性?
【发布时间】:2011-01-19 13:35:33
【问题描述】:

我在同时使用 GDI 和 GDI+ 进行绘图时遇到问题。页面转换——尤其是缩放——似乎在两者之间有点偏离。除了SetViewportExtSetWindowExt 之外,GDI 上下文的哪些属性会影响输出的缩放?

代码几乎只使用 GDI 进行绘图,但在少数需要其特性(半透明)的情况下使用 GDI+。它使用SetViewportExtSetWindowExtSetViewportOrg 来启用缩放和滚动。

当需要 GDI+ 时,我在 HDC 周围构造一个 Gdiplus::Graphics 对象并进行绘图。我假设这会使图形上下文包装设备上下文并将其渲染传递给设备上下文。如果我提取 GDI+ 图形上下文的变换矩阵,我看到它是单位矩阵,所以缩放是在其他地方完成的(我猜是在设备上下文中)。

我设计了一个简单的测试,我用 GDI 和 GDI+ 绘制相同的矩形数组,以确保在这两种情况下所有的转换都是相同的。代码sn-p如下:

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()));
}

结果在这里:(蓝色虚线是GDI画的,灰色是GDI+画的)

我可以清楚地看到两个坐标系是不同的。我预计会有一些舍入误差,但不是这里看到的缩放误差。此外,当我更改缩放系数时,GDI+ 根据缩放在两个方向上跳跃±4 像素左右。这也在屏幕截图中突出显示,因为与 GDI 矩形相比,GDI+ 矩形在 X 轴上具有正偏移,在 Y 轴上具有负偏移。

  • 有人知道这里发生了什么吗?

  • 我将如何着手调查/调试此问题?这发生在 Windows 的内部,所以很遗憾我无法调试它。

作为参考,这是我的 viewport/window org/ext 的样子:

Window Ext: (134000, 80500)
Window Org: (0, 0)
Viewport Ext: (1452 872)
Viewport Org: (35 35)

更新:

我已经解决了这个问题,但它并不漂亮。基本做法是:

  1. 在屏幕空间中获取两个坐标(原点和第二个适当的点),并使用 GDI(DPtoLP 函数)将它们转换为逻辑坐标。

  2. 将 GDI 转换重置为 MM_TEXT

  3. 使用变换后的点为 GDI+ 构造一个变换矩阵,代表相同的变换

  4. 最后使用这个矩阵构造一个具有正确转换的 GDI+ 上下文。

这有点小技巧,但它确实有效。不过,我仍然不知道为什么两者之间存在差异。至少它表明可能有一个 GDI+ 上下文模仿 GDI 转换。

【问题讨论】:

    标签: c++ windows gdi+ gdi


    【解决方案1】:

    要记住的一点是,大多数 GDI 通常在硬件上运行(即 GDI 函数映射到在芯片上实现某些功能的显示驱动程序) GDI+ 应该获得硬件加速,但它仍然是一个单独的软件渲染器。

    尝试通过 GDI+ 和 GDI 手动设置几个像素,看看它们是否不同。

    也许您的特定显卡转换坐标的方式与它在 GDI+ 中发生的方式不同

    【讨论】:

    • 我已经收集了很多,但我仍然不明白为什么它从 GDI 到 GDI+ 差异如此之大。 (除非硬件加速是由显卡驱动实现的,而且它的实现有问题,但我觉得不太可能。)
    • 我想用实际的代码做实验,你能发布一个最简单的完整的编译示例吗?
    【解决方案2】:

    我们也遇到过同样的问题。

    (背景:GDI 几乎适用于所有内容,而且对于需要呈现 1000 个文本单元格的电子表格样式的显示来说似乎要快得多。但是我们需要 GDI+ 来显示 .jpg 的。)

    在屏幕上显示内容时,GDI+ 缩放似乎是正确的。我们有一个打印预览功能,它使用坐标变换让应用程序使用打印机坐标进行渲染,但显示在屏幕上。一切正常,直到我们将其发送到实际的打印机(或 PDF 编写器)时,缩放被塞满。

    经过一个人周的工作(并从您那里得到解决方案的提示),这是我们的理解:

    GDI+ 中有一个错误,当您调用:“Graphics(HDC)”(从 GDI 设备上下文创建图形对象)时,HDC 来自打印机或软件打印机,例如6000 x 4000 像素分辨率,然后 GDI+ 会忽略 HDC 正在使用这种大分辨率这一事实,而是应用自己的大约 1000 x 800 像素的分辨率。

    因此,您的解决方法可能是解决问题的正确和最佳解决方案。

    我们的解决方案类似但略有不同,因为我们实际上并不需要任何坐标变换:

            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);
    

    在许多打印机驱动程序上,这些比例因子似乎非常接近“6”。

    【讨论】:

      【解决方案3】:

      我找到了解决打印时出现问题的方法。请注意,示例中的图形对象没有设置任何世界空间变换,因此直接在页面空间中进行绘制。

      将页面单位设置为英寸,然后将坐标转换为英寸似乎无需太多额外工作即可解决绘图问题。使用不同 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);
      

      【讨论】:

        【解决方案4】:

        简答:致电graphics.SetPageUnit(Gdiplus::UnitPixel)

        我遇到了与https://stackoverflow.com/a/4894969/700027 相同的问题:打印时,GDI+ (Gdiplus::Graphics) 的坐标与 GDI (HDC) 的坐标不匹配。

        graphics.GetPageUnit() 正在返回 UnitDisplayUnitDisplay的文档是:

        指定显示单位。例如,如果显示设备是显示器,那么单位是 1 像素。

        我错误地认为对于打印机,UnitDisplay 将使用打印机点。经过一番挣扎,我终于发现它实际上使用了 1/100 英寸,原因不明。如果我使用 Gdiplus::UnitPixel 则 GDI+ 坐标与 GDI 坐标相同。

        【讨论】:

          猜你喜欢
          • 2019-04-07
          • 2013-07-22
          • 2010-12-14
          • 1970-01-01
          • 2014-07-31
          • 2012-05-10
          • 1970-01-01
          • 2014-09-23
          • 1970-01-01
          相关资源
          最近更新 更多