You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DrawPath绘制圆角矩形形状不符合预期问题求助

圆角矩形打印线条异常问题

我尝试绘制圆角矩形,但打印布局时线条出现异常:错误效果为圆角处线条断裂、衔接不自然,边框粗细不均;正确效果应为圆角平滑衔接、边框线条均匀连贯的圆角矩形。

相关代码片段

边框绘制方法

private void DrawBorder(LayoutObject object, Graphics graphics)
{
    var objectX = object.X;
    var objectY = object.Y;
    if (_rotate180)
    {
        (objectX, objectY) = object.CalculateRotation180(_layoutHeight, _layoutWidth);
    }

    // Calculate object dimensions based on graphics DPI
    var xDpi = objectX * graphics.DpiX;
    var yDpi = objectY * graphics.DpiY;

    var widthDpi = object.Width * graphics.DpiX;
    var heightDpi = object.Height * graphics.DpiY;

    var borderWidthX = object.BorderThickness * graphics.DpiX;
    var borderWidthY = object.BorderThickness * graphics.DpiY;

    var cornerRadiusX = object.CornerRadius * graphics.DpiX;
    var cornerRadiusY = object.CornerRadius * graphics.DpiY;

    var rectangle = new RectangleF(xDpi, yDpi, widthDpi, heightDpi);

    // Draw the border (with possible rounding)
    if (borderWidthX > 0 || borderWidthY > 0)
    {
        if (!string.IsNullOrWhiteSpace(object.BorderColor))
        {
            var brush = object.BorderColor.FromRgbaToSolidBrush();

            if (_rotate180)
            {
                graphics.RotateCenter(180.0F, rectangle);
            }

            if (object.Orientation > 0)
            {
                graphics.RotateCenter(object.Orientation, rectangle);
            }

            // Define the rectangle for drawing the border based on the original rectangle.
            var borderRectangle = new RectangleF(xDpi + (borderWidthX / 2), yDpi + (borderWidthY / 2), widthDpi - borderWidthX, heightDpi - borderWidthY);

            if (cornerRadiusX > 0 || cornerRadiusY > 0)
            {
                // Draw the rounded border.
                cornerRadiusX -= borderWidthX / 2;
                cornerRadiusY -= borderWidthY / 2;

                graphics.DrawRoundedCornerRectangle(borderRectangle, new Pen(brush, borderWidthX), cornerRadiusX, cornerRadiusY);
            }
            else
            {
                graphics.DrawRectangle(new Pen(brush, borderWidthX), borderRectangle.X, borderRectangle.Y, borderRectangle.Width, borderRectangle.Height);
            }

            graphics.ResetTransform();
        }
    }
}

圆角矩形绘制扩展方法

public static void DrawRoundedCornerRectangle(this Graphics graphics, RectangleF rectangle, Pen pen, float xradius, float yradius)
{
    using (GraphicsPath path = MakeRoundedRect(rectangle, xradius, yradius))
    {
        graphics.DrawPath(pen, path);
    }
}

private static GraphicsPath MakeRoundedRect(RectangleF rect, float xradius, float yradius)
{
    // Make a GraphicsPath to draw the rectangle.
    var path = new GraphicsPath();

    // Upper left corner.
    RectangleF corner = new RectangleF(rect.X, rect.Y, 2 * xradius, 2 * yradius);
    path.AddArc(corner, 180, 90);

    // Upper right corner.
    corner = new RectangleF(rect.Right - 2 * xradius, rect.Y, 2 * xradius, 2 * yradius);
    path.AddArc(corner, 270, 90);

    // Lower right corner.
    corner = new RectangleF(rect.Right - 2 * xradius, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius);
    path.AddArc(corner, 0, 90);

    // Lower left corner.
    corner = new RectangleF(rect.X, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius);
    path.AddArc(corner, 90, 90);

    // Join with the start point.
    path.CloseFigure();

    return path;
}

问题排查与修复方案

核心问题分析

  1. 旋转时机错误:在计算borderRectangle前就对Graphics应用旋转,导致后续基于原始坐标计算的边框位置偏移,旋转后绘制的线条出现错位。
  2. 圆角半径无边界检查:直接减去borderWidthX/2可能导致圆角半径为负,引发绘制异常;同时未限制圆角半径不超过矩形边长的一半,导致圆弧绘制错误。
  3. 线条衔接未优化:Pen未设置LineJoin属性,圆角与直线衔接处容易出现断裂;MakeRoundedRect仅添加圆弧未显式连接直线段,路径连续性不足。
  4. 变换管理不严谨:直接调用ResetTransform可能影响其他绘制操作,应使用Graphics.Save()和状态恢复来管理变换。

修复后的代码

优化后的DrawBorder方法

private void DrawBorder(LayoutObject obj, Graphics graphics)
{
    var objectX = obj.X;
    var objectY = obj.Y;
    if (_rotate180)
    {
        (objectX, objectY) = obj.CalculateRotation180(_layoutHeight, _layoutWidth);
    }

    // 基于DPI统一计算尺寸
    var xDpi = objectX * graphics.DpiX;
    var yDpi = objectY * graphics.DpiY;
    var widthDpi = obj.Width * graphics.DpiX;
    var heightDpi = obj.Height * graphics.DpiY;
    var borderWidth = obj.BorderThickness * graphics.DpiX;
    var cornerRadiusX = obj.CornerRadius * graphics.DpiX;
    var cornerRadiusY = obj.CornerRadius * graphics.DpiY;

    var rectangle = new RectangleF(xDpi, yDpi, widthDpi, heightDpi);

    if (borderWidth > 0 && !string.IsNullOrWhiteSpace(obj.BorderColor))
    {
        var brush = obj.BorderColor.FromRgbaToSolidBrush();
        // 设置Pen的线条衔接为圆角,避免断裂
        var pen = new Pen(brush, borderWidth) { LineJoin = LineJoin.Round };

        // 先计算正确的边框矩形,再执行旋转
        var borderRectangle = new RectangleF(
            xDpi + borderWidth / 2,
            yDpi + borderWidth / 2,
            widthDpi - borderWidth,
            heightDpi - borderWidth
        );

        // 保存图形状态,自动恢复变换
        using (var state = graphics.Save())
        {
            if (_rotate180)
            {
                graphics.RotateCenter(180.0F, rectangle);
            }

            if (obj.Orientation > 0)
            {
                graphics.RotateCenter(obj.Orientation, rectangle);
            }

            if (cornerRadiusX > 0 || cornerRadiusY > 0)
            {
                // 确保圆角半径不小于0,且不超过矩形边长的一半
                var adjustedRadiusX = Math.Max(0, Math.Min(cornerRadiusX - borderWidth / 2, borderRectangle.Width / 2));
                var adjustedRadiusY = Math.Max(0, Math.Min(cornerRadiusY - borderWidth / 2, borderRectangle.Height / 2));
                graphics.DrawRoundedCornerRectangle(borderRectangle, pen, adjustedRadiusX, adjustedRadiusY);
            }
            else
            {
                graphics.DrawRectangle(pen, borderRectangle.X, borderRectangle.Y, borderRectangle.Width, borderRectangle.Height);
            }
        }
    }
}

优化后的圆角路径生成方法

public static void DrawRoundedCornerRectangle(this Graphics graphics, RectangleF rectangle, Pen pen, float xradius, float yradius)
{
    using (GraphicsPath path = MakeRoundedRect(rectangle, xradius, yradius))
    {
        graphics.DrawPath(pen, path);
    }
}

private static GraphicsPath MakeRoundedRect(RectangleF rect, float xradius, float yradius)
{
    var path = new GraphicsPath();
    // 限制圆角半径不超过矩形对应边长的一半
    xradius = Math.Min(xradius, rect.Width / 2);
    yradius = Math.Min(yradius, rect.Height / 2);

    // 左上圆角 + 上边直线
    path.AddArc(rect.X, rect.Y, 2 * xradius, 2 * yradius, 180, 90);
    path.AddLine(rect.X + xradius, rect.Y, rect.Right - xradius, rect.Y);

    // 右上圆角 + 右边直线
    path.AddArc(rect.Right - 2 * xradius, rect.Y, 2 * xradius, 2 * yradius, 270, 90);
    path.AddLine(rect.Right, rect.Y + yradius, rect.Right, rect.Bottom - yradius);

    // 右下圆角 + 下边直线
    path.AddArc(rect.Right - 2 * xradius, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius, 0, 90);
    path.AddLine(rect.Right - xradius, rect.Bottom, rect.X + xradius, rect.Bottom);

    // 左下圆角 + 左边直线
    path.AddArc(rect.X, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius, 90, 90);
    path.AddLine(rect.X, rect.Bottom - yradius, rect.X, rect.Y + yradius);

    path.CloseFigure();
    return path;
}

修复要点说明

  • 调整旋转顺序:先计算边框的正确位置,再应用旋转变换,避免坐标偏移。
  • 优化线条衔接:给Pen设置LineJoin = LineJoin.Round,确保圆角与直线衔接平滑。
  • 边界检查:限制圆角半径的取值范围,避免出现负数或超出矩形尺寸的情况。
  • 显式路径构造:在圆角路径中添加明确的直线段,确保路径连续无断裂。
  • 安全管理变换:使用Graphics.Save()和using块自动恢复图形状态,替代直接调用ResetTransform,避免副作用。

内容的提问来源于stack exchange,提问作者Francesco Vargas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:15:54