在进行GDI+图形编程时,经常需要绘制圆角矩形。然而,可能已经注意到,即使定义了完美的圆角矩形,最终在像素级别上绘制出来的结果往往并不完全对称。这种不对称性通常在以下情况下出现:当圆角半径等于10、笔刷宽度大于1或者使用了FillPath
方法时。这种不对称性在圆角半径大于15时似乎不太明显,可能是因为有更多的像素可以利用,或者这种不对称性在视觉上不那么明显。
由于圆角矩形并不是GDI+的原生形状,通常需要使用GraphicsPath
或其他机制来创建。无论是使用AddArc
、AddBezier
、多边形点、变换等方法,只要满足上述任一条件,不对称性就会出现。这些方法定义点是准确的,问题出现在形状渲染(绘制或填充)时。不猜测这种不对称性的底层原因。
CRoundRect
类完全在头文件中实现,提供了以下基本功能:GetRoundRectPath()
、DrawRoundRect()
和FillRoundRect()
。为了获得对称的结果,需要三个变通方法。
这个函数使用AddArc
方法定义圆角矩形路径。第一个变通方法处理了半径为10的特殊情况。它在战略点偏移了弧的矩形并增加了其大小。没有很好的理论来解释为什么这样做有效,或者为什么只有半径为10时才需要这样做。
void GetRoundRectPath(GraphicsPath *pPath, Rect r, int dia) {
// diameter can't exceed width or height
if (dia > r.Width) dia = r.Width;
if (dia > r.Height) dia = r.Height;
// define a corner
Rect Corner(r.X, r.Y, dia, dia);
// begin path
pPath->Reset();
// top left
pPath->AddArc(Corner, 180, 90);
// tweak needed for radius of 10 (dia of 20)
if (dia == 20) {
Corner.Width += 1;
Corner.Height += 1;
r.Width -= 1; r.Height -= 1;
}
// top right
Corner.X += (r.Width - dia - 1);
pPath->AddArc(Corner, 270, 90);
// bottom right
Corner.Y += (r.Height - dia - 1);
pPath->AddArc(Corner, 0, 90);
// bottom left
Corner.X -= (r.Width - dia - 1);
pPath->AddArc(Corner, 90, 90);
// end path
pPath->CloseFigure();
}
这个函数使用传递的矩形、半径、笔颜色和笔宽度绘制圆角矩形。第二个变通方法涉及使用笔宽度为1,并绘制“宽度”数量的矩形,每次递减矩形的大小。这本身是不够的,因为它会在角落留下空洞。相反,它只减少x,绘制矩形,然后减少y,再次绘制。
void DrawRoundRect(Graphics* pGraphics, Rect r, Color color, int radius, int width) {
int dia = 2 * radius;
// set to pixel mode
int oldPageUnit = pGraphics->SetPageUnit(UnitPixel);
// define the pen
Pen pen(color, 1);
pen.SetAlignment(PenAlignmentCenter);
// get the corner path
GraphicsPath path;
// get path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
// if width > 1
for (int i = 1; i < width; i++) {
// left stroke
r.Inflate(-1, 0);
// get the path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
// up stroke
r.Inflate(0, -1);
// get the path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
}
// restore page unit
pGraphics->SetPageUnit((Unit)oldPageUnit);
}
这个函数使用传递的矩形、半径和画刷颜色填充圆角矩形。第三个变通方法涉及填充矩形,然后绘制边框以修复边缘。
void FillRoundRect(Graphics* pGraphics, Brush* pBrush, Rect r, Color border, int radius) {
int dia = 2 * radius;
// set to pixel mode
int oldPageUnit = pGraphics->SetPageUnit(UnitPixel);
// define the pen
Pen pen(border, 1);
pen.SetAlignment(PenAlignmentCenter);
// get the corner path
GraphicsPath path;
// get path
GetRoundRectPath(&path, r, dia);
// fill
pGraphics->FillPath(pBrush, &path);
// draw the border last so it will be on top
pGraphics->DrawPath(&pen, &path);
// restore page unit
pGraphics->SetPageUnit((Unit)oldPageUnit);
}
这个函数有一个替代版本,它接受一个Brush
作为其参数之一。这是必要的,如果想用除了SolidBrush
之外的东西填充。颜色参数是必需的,这样函数就知道要制作什么颜色的边框。这个函数也可以在一次调用中用于边框和填充,假设想要边框宽度为一。
void FillRoundRect(Graphics* pGraphics, Rect r, Color color, int radius) {
SolidBrush sbr(color);
FillRoundRect(pGraphics, &sbr, r, color, radius);
}