背景
我正在使用传统的MFC应用程序,它使用GDI绘制其内容。
我需要绘制圆角矩形,其中每个角都有(可能)不同的半径。
这意味着我无法再使用RoundRect
并且必须使用ArcTo
滚动自己。
我正在使用SetWindowExtEx
,SetWindowOrgEx
,SetViewportExtEx
和SetViewportOrgExt
来实施缩放。
这在大多数情况下都能正常工作。
问题
在某些缩放级别上,我的代码无法构建圆形轮廓的正确路径。
以下屏幕截图是用于创建路径的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);
答案 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
替换弧。