GDI+中的圆角矩形绘制技巧

在进行GDI+图形编程时,经常需要绘制圆角矩形。然而,可能已经注意到,即使定义了完美的圆角矩形,最终在像素级别上绘制出来的结果往往并不完全对称。这种不对称性通常在以下情况下出现:当圆角半径等于10、笔刷宽度大于1或者使用了FillPath方法时。这种不对称性在圆角半径大于15时似乎不太明显,可能是因为有更多的像素可以利用,或者这种不对称性在视觉上不那么明显。

由于圆角矩形并不是GDI+的原生形状,通常需要使用GraphicsPath或其他机制来创建。无论是使用AddArcAddBezier、多边形点、变换等方法,只要满足上述任一条件,不对称性就会出现。这些方法定义点是准确的,问题出现在形状渲染(绘制或填充)时。不猜测这种不对称性的底层原因。

CRoundRect类

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); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485