圆角边框的绘制方法

发表于2019年07月14日

LCUI 的圆角边框渲染一直没有实现,因为在这之前觉得这个功能需要花费大量的连续时间来开发和调试,长时间集中精力搞一个功能有点困难,所以就搁置了,现在准备完善它,毕竟一个 GUI 开发库连圆角边框这种常见效果都不能实现那确实有点水。首先,以浏览器上的各种圆角效果作为参考:

Border

从上图可以看出,两条边框的颜色在边界框对角线处贴合,外边界呈圆形,当两条相交的边框宽度不同时,内边界呈椭圆形。得出这样的规律后,绘制时的主要操作也就是根据当前 y 轴坐标计算出外边界、分割线、内边界的 x 轴坐标,然后填充像素:圆外的像素填充透明色,分割线左边的用左边框颜色,分割线右边用上边框颜色,直到内边界为止。

怎么计算这些坐标?套用数学题的表达方式,需要解决的问题有以下三个:

  1. 已知圆心坐标为 (0,0),半径为 r,圆上点 P 的 y 轴坐标为 y,求点 P 的 x 轴坐标

    ∵ x² + y² = r²
    ∴ x = sqrt(r² - y²)
    
  2. 已知两点 A(x1, y1)、B(x2, y2),点 C 的 y 轴坐标为 y3,且位于直线 AB 上,求点 C 的 x 轴坐标

    ∵ (x2 - x1) / (y2 - y1) == (x2 - x3) / (y2 - y3)
    ∴ x3 = (x2 - x1) / (y2 - y1) * (y2 - y3)
    
  3. 已知椭圆长轴长度为 2a,短轴长度为 2b, 圆上点 P 的 y 轴坐标为 y,求点 P 的 x 轴坐标

    ∵ x² / a² + y² / b² = 1
    ∴ x = sqrt((1 - y² / b²) * a²)
    

接下来就是绘制了,如果直接套用平面坐标系来转换像素坐标的话,实际绘制出来的圆并不圆,仔细看会发现右边界被裁剪掉了 1px,坐标计算有问题。

图形绘制问题只凭脑子想效率会很低,可以使用画图工具画个示例图来辅助思考:

Border pixels

上图是 10x10 像素点阵,可以看出红色部分就是圆占用的像素点,填充像素点时使用的是屏幕坐标系,那么,需要解决的问题有以下四个:

  1. 如何计算实际绘制的圆的半径?

    radius -= 0.5
    
  2. 已知平面直角坐标系的原点 O 在屏幕坐标系上的坐标 (x, y),如何转换屏幕坐标系中的坐标?

    x = x - O.x
    y = O.y - y
    
  3. 在屏幕坐标系中,已知圆心 O 的坐标 (4.5, 4.5),半径 r 为 4.5,圆上点的 y 轴坐标为 4.5,求该点的 x 轴坐标

    ∵ (x - O.x)² + (O.y - y)² = r²
    ∴ x = O.x + sqrt(r² - (O.y - y)²)
    ∴ x = 4.5 + sqrt(4.5² - (4.5 - 4.5)²)
    ∴ x = 9
    
  4. 在屏幕坐标系中,已知圆心 O 的坐标 (4.5, 4.5),半径 r 为 4.5,圆上点在区域 rect (5, 0, 5, 5) 中的 y 轴相对坐标为 4,求该点的 x 轴相对坐标

    ∵ (rect.x + x - O.x)² + (O.y - rect.y - y)² = r²
    ∴ x = sqrt(r² - (O.y - rect.y - y)²) + O.x - rect.x
    ∴ x = sqrt(4.5² - (4.5 - 0 - 4)²) + 4.5 - 5
    ∴ x = sqrt(4.5² - 0.5²) - 0.5
    ∴ x = 3.97213