为什么ArcTo有时不会更新当前位置

时间:2014-10-28 18:27:39

标签: winapi mfc gdi clipping

背景

我正在使用传统的MFC应用程序,它使用GDI绘制其内容。

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

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

问题

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

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

我的猜测是,由于舍入错误,弧线太小,并且被GDI完全跳过。 我发现这很难相信,因为它对于比这里所示的缩放因子更小的缩放因子是正确的。

正常工作:
Correct

错过一个角落:
enter image description here

守则

我试图减少重现它所需的代码,并最终得到以下结果。请注意,屏幕截图中的数字是唯一变量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);

1 个答案:

答案 0 :(得分:2)

这是一个简单的代码来说明问题:

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时,坐标转换会将第二个圆弧的矩形折叠为空矩形。您可以通过在代码中添加以下内容来自行查看,以转换第二个圆弧的坐标: / p>

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 bug。

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

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