【问题标题】:GDI - Clipping is not working when used on a printer device contextGDI - 在打印机设备上下文中使用时,剪辑不起作用
【发布时间】:2018-02-01 00:01:04
【问题描述】:

我正在使用 Embarcadero RAD Studio C++ builder XE7 编译器。在一个应用程序项目中,我同时使用 Windows GDI 和 GDI+ 来绘制多个设备上下文。

我的绘图内容是这样的:

在上面的示例中,文本背景和用户图片是使用 GDI+ 绘制的。用户图片也用圆角路径剪裁。所有其他项目(文本和表情符号)都是使用 GDI 绘制的。

当我在屏幕 DC 上绘制时,一切正常。

现在我想绘制一个打印机设备上下文。无论我用于测试的是 Windows 10 中可用的新“导出为 PDF”打印机设备。我准备我的设备上下文以通过这种方式在 A4 视口上绘制:

HDC GetPrinterDC(HWND hWnd) const
{
    // initialize the print dialog structure, set PD_RETURNDC to return a printer device context
    ::PRINTDLG pd  = {0};
    pd.lStructSize = sizeof(pd);
    pd.hwndOwner   = hWnd;
    pd.Flags       = PD_RETURNDC;

    // get the printer DC to use
    ::PrintDlg(&pd);

    return pd.hDC;
}

...

void Print()
{
    HDC hDC = NULL;

    try
    {
        hDC = GetPrinterDC(Application->Handle);

        const TSize srcPage(793, 1123);
        const TSize dstPage(::GetDeviceCaps(hDC, PHYSICALWIDTH), ::GetDeviceCaps(hDC, PHYSICALHEIGHT));
        const TSize pageMargins(::GetDeviceCaps(hDC, PHYSICALOFFSETX), ::GetDeviceCaps(hDC, PHYSICALOFFSETY));

        ::SetMapMode(hDC, MM_ISOTROPIC);
        ::SetWindowExtEx(hDC, srcPage.Width, srcPage.Height, NULL);
        ::SetViewportExtEx(hDC, dstPage.Width, dstPage.Height,  NULL);
        ::SetViewportOrgEx(hDC, -pageMargins.Width, -pageMargins.Height, NULL);

        ::DOCINFO di = {sizeof(::DOCINFO), config.m_FormattedTitle.c_str()};

        ::StartDoc (hDC, &di);

        // ... the draw function is executed here ...

        ::EndDoc(hDC);
        return true;
    }
    __finally
    {
        if (hDC)
            ::DeleteDC(hDC);
    }
}

在 StartDoc() 和 EndDoc() 函数之间执行的绘图函数与我在屏幕上使用的绘图函数完全相同。唯一的区别是我在整个页面上添加了一个全局剪切矩形,以避免在尺寸太大时绘图与页边距重叠,例如当我在第一个下多次重复上面的绘图时。 (这个是实验性的,后面我会加一个分页的过程,不过这个暂时不是问题)

这是我的剪辑功能:

int Clip(const TRect& rect, HDC hDC)
{
    // save current device context state
    int savedDC = ::SaveDC(hDC);

    HRGN pClipRegion = NULL;

    try
    {
        // reset any previous clip region
        ::SelectClipRgn(hDC, NULL);

        // create clip region
        pClipRegion = ::CreateRectRgn(rect.Left, rect.Top, rect.Right, rect.Bottom);

        // select new canvas clip region
        if (::SelectClipRgn(hDC, pClipRegion) == ERROR)
        {
            DWORD error = ::GetLastError();
            ::OutputDebugString(L"Unable to select clip region - error - " << ::IntToStr(error));
        }
    }
    __finally
    {
        // delete clip region (it was copied internally by the SelectClipRgn())
        if (pClipRegion)
            ::DeleteObject(pClipRegion);
    }

    return savedDC;
}

void ReleaseClip(int savedDC, HDC hDC)
{
    if (!savedDC)
        return;

    if (!hDC)
        return;

    // restore previously saved device context
    ::RestoreDC(hDC, savedDC);
}

如上所述,我预计我的页面周围会有剪辑。然而结果只是一个空白页。如果我绕过剪切功能,所有打印都正确,除了绘图可能在页边距上重叠。另一方面,如果我在屏幕上的任意矩形上应用剪辑,一切正常。

我的剪辑有什么问题?为什么我启用后页面完全崩溃了?

【问题讨论】:

  • 一些希望有用的提示:Clip(rect) 函数中的 rect 参数是什么,它使用什么单位,相对于什么?你确定在 draw 和 clip 之间使用兼容的单位吗?
  • 一般来说SelectClipRgn在这方面应该没有问题。你有SelectClipRgn(pCanvas-&gt;Handle, ...)SelectClipRgn(hDC, ...) pCanvas-&gt;HandlehDC 是同一个东西吗?我不知道 Delphi,但似乎您创建了 HRGN 并立即将其删除,所以它不会做任何事情。您应该制作 MCVE 来演示问题。
  • 你说得对,这是我的复制错误。实际上 pCanvas->Handle 是 C++ Builder 编译器提供的 TCanvas 对象的一个​​属性。该对象是 GDI 的包装器,而 Handle 属性实际上是设备上下文 (HDC)。当我复制函数时,为了清楚起见,我直接通过 HDC 更改了函数参数中的 TCanvas,但我省略了一个实例。我修改了源代码,谢谢你的意见。
  • @Niki:rect 参数是 C++ Builder 编译器提供的另一个对象(我在问题的开头提到了这个编译器的用法)。它只是一个包含 4 个参数(左、上、右、下)描述矩形的结构。 AFAIK 它是 GDI RECT 结构的包装器。 Clip 函数用于声明允许绘图的文档的矩形部分,即没有边距的页面。坐标系由视口函数定义。由于剪裁和绘图应用在相同的上下文中,它们的单位通常是兼容的。
  • @Barmak Shemirani:关于 HRGN,这不是 Delphi 对象,而是 GDI 句柄。关于删除,SelectClipRgn() 的 MS 文档说:“仅使用所选区域的副本。可以为任意数量的其他设备上下文选择区域本身,也可以将其删除。”据我了解,这意味着无论何时分配该区域都会在设备内部复制,并且可能会释放他的源区域,这就是我这样做的原因。

标签: printing gdi+ clipping gdi


【解决方案1】:

所以我发现了问题所在。 Niki 接近解决方案。裁剪功能似乎总是以像素为单位应用于页面,忽略了坐标系和视口定义的单位。

在我的例子中,传递给 CreateRectRgn() 函数的值是错误的,因为它们没有被视口转换,尽管在设备上下文中设置视口之后应用了剪辑。

这使问题的识别变得困难,因为在读取代码时剪辑显示为已转换,因为它是在视口之后应用的,就在处理绘图之前。

我不知道这是 GDI 错误还是希望的行为,但不幸的是,我从未在我阅读的所有有关剪辑的文档中看到此细节。虽然在我看来,知道剪辑不受视口影响很重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-31
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多