【问题标题】:Why does ArcTo sometimes not update the current position为什么 ArcTo 有时不更新当前位置
【发布时间】:2014-10-28 18:27:39
【问题描述】:

背景

我正在使用一个使用 GDI 绘制其内容的旧版 MFC 应用程序。

我需要绘制圆角矩形,其中每个角都有(可能)不同的半径。 这意味着我不能再使用RoundRect 并且必须使用ArcTo 自己滚动。 我正在使用SetWindowExtExSetWindowOrgExSetViewportExtExSetViewportOrgExt 来实现缩放。

这在大多数情况下都可以正常工作。

问题

在某些缩放级别上,我的代码无法构建正确的圆形轮廓路径。

以下屏幕截图是我的RoundRect 代码用于创建路径,然后用于剪切更大的矩形(以了解它的形状)。 此路径创建的剪切区域有时会丢失一个角、剪切所有内容(两个丢失的角?)或什么都不剪切。

我的猜测是由于舍入误差,圆弧太小,被 GDI 一起跳过。 我觉得这很难相信,因为它可以在比这里图片更小的缩放系数下正常工作。

正常工作:

缺角:

守则

我试图减少重现它所需的代码,并最终得到以下结果。请注意,屏幕截图中的数字是唯一变量zoomFactor 的值。 您应该可以将此代码粘贴到新创建的 Win32 应用程序项目的 OnPaint 函数中,并手动将 zoomFactor 声明为常量。

SetMapMode(hdc, MM_ISOTROPIC);
SetWindowOrgEx(hdc, 0, 40, nullptr);
SetWindowExtEx(hdc, 8000, 6000, nullptr);
SetViewportOrgEx(hdc, 16, 56, nullptr);
SetViewportExtEx(hdc, 16 + (396)*zoomFactor/1000,
                      48 + (279)*zoomFactor/1000, nullptr);

BeginPath(hdc);

MoveToEx(hdc, 70, 1250, nullptr);
ArcTo(hdc,
    50,   1250, 90,   1290,
    70,   1250,
    50,   1270);
ArcTo(hdc,
    50,   2311, 90,   2351,
    50,   2331,
    70,   2351);
ArcTo(hdc,
    1068, 2311, 1108, 2351,
    1088, 2351,
    1108, 2331);
ArcTo(hdc,
    1068, 1250, 1108, 1290,
    1108, 1270,
    1088, 1250);

CloseFigure(hdc);
EndPath(hdc);
SelectClipPath(hdc, RGN_AND);

HBRUSH br = CreateSolidBrush(RGB(255,0,255));
const RECT r = {0, 0, 8000, 6000};
FillRect(hdc, &r, br);

【问题讨论】:

  • 你有没有考虑为每个角画一个单独的RoundRect,然后填充剩余的中心垂直和水平条纹?
  • @user1793036,是的。我有,但我需要为大纲生成一条路径,这是我遇到的问题。画的东西很好。

标签: winapi mfc gdi clipping


【解决方案1】:

这里有一段更简单的代码来说明问题:

const int r = 20;
MoveToEx(hdc, 200, 100, 0);
BOOL b = ArcTo(hdc,
    100 + 2 * r, 100,
    100,         100 + 2 * r,
    100 + r,     100,
    100,         100 + r);
POINT p;
GetCurrentPositionEx(hdc, &p);

这会绘制一个半径为 r 的角。这适用于 r 的非零值,并且位置 p 被正确更新以匹配弧的末端:(100, 100+r),给予或获取一个像素。

但是,当 r 为零时,ArcTo 返回 TRUE 但位置未更新:p 包含 (200,100) 的起始位置。

文档指出“如果没有发生错误,则将当前位置设置为弧的终点。”该函数返回了TRUE,表示成功,所以位置应该已经更新了。

在我看来这是一个错误。该函数应该返回FALSE,因为矩形是空的,所以没有弧,因此没有明确定义的端点。但是,如果函数返回 TRUE 并更新当前位置以匹配参数列表中的最终坐标对,则在实践中会更有用。但它不做这些事情。编辑:在您的情况下,更好的实现是在转换为设备坐标之前计算逻辑坐标中的弧端点,但 GDI 通常不会像这样工作。

问题出现在您的代码中,因为当缩放为 266 时,您的坐标转换将第二个弧的矩形折叠为一个空矩形。您可以通过在代码中添加以下内容来自己查看这一点,以转换第二个弧的坐标:

POINT points[4] = {{50,2311},{90,2351},{50,2331},{70,2351}};
LPtoDP(hdc, points, 4);

缩放设置为 266 时,点将转换为 (17,90)、(17,91)、(17,91)、(17,91),因此矩形没有宽度且为空。你遇到了ArcTo 错误。

当舍入恰好将 x 坐标放入相邻的整数而不是相同的整数时,我猜它适用于较小的缩放。

一个简单的解决方法是创建一个MyArcTo 函数,当圆弧太小而无法看到时,用LineTo 替换它。

【讨论】:

  • 谢谢。我在生产代码中看到的问题是舍入错误,将端点放置在边界矩形的中心。它引起了同样的问题;无声的失败。不过,您确实使我找到了解决方法;使用GetCurrentPositionEx查看当前位置是否改变,如果没有则使用LineToEx绘制直角。
猜你喜欢
  • 2020-04-18
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
  • 2015-06-07
  • 2018-01-13
  • 1970-01-01
  • 2018-12-09
  • 2012-03-17
相关资源
最近更新 更多