19 个版本

0.7.0 2022年6月1日
0.6.0 2020年9月19日
0.5.0 2020年5月19日
0.3.1 2019年3月7日
0.0.10 2017年10月25日

#203 in 图像

Download history 48/week @ 2024-03-10 4/week @ 2024-03-17 68/week @ 2024-03-31 3/week @ 2024-04-07 6/week @ 2024-04-14 21/week @ 2024-04-21 13/week @ 2024-04-28 25/week @ 2024-05-05 25/week @ 2024-05-12 8/week @ 2024-05-19 37/week @ 2024-05-26 25/week @ 2024-06-02 26/week @ 2024-06-09 32/week @ 2024-06-16 4/week @ 2024-06-23

94 每月下载量
6 个crate(直接使用 4 个) 中使用

MIT/Apache

77KB
2K SLoC

footile

一个用 Rust 编写的 2D 矢量图形库。它使用 pix 来处理底层的光栅图像。

请参阅 文档 了解示例和 API 使用方法。

光栅化:鸟瞰图

这里没有新意——这只是一个代码指南。

所以,我们有一个由线、贝塞尔样条、弧等组成的 2D 路径,我们想要从它中制作一个高质量的栅格图像。但是如何实现呢?

模块

  • path:定义路径操作和 Path2D
  • geom:由 plotterstroker 使用的点(Pt)和变换
  • plotter:定义 Plotter 结构体并简化曲线
  • stroker:为绘图器创建 描边 路径
  • fixed:定义 Fixed 结构体,由 fig 模块使用
  • fig:将路径光栅化

曲线简化

在这里,“简化”指的是用一系列线段来近似曲线,与疫情应对无关。

目前我们使用 De Casteljau 描述的递归算法。如果确定这是一个瓶颈,这里可能有机会进行优化。还有一点需要注意:这种方法可能会因输入数据错误而引起堆栈溢出。

一旦完成,我们将得到一系列线段,形成一个或多个封闭的多边形。

顶点排序

在下一步中,我们创建一个按(Y,X)顺序排序的顶点列表。这是必需的,因为我们将逐行将多边形扫描到网格上。

每条路径都有一个绕行顺序:顺时针或相反方向,有时也称为逆时针反时针。为了避免这种争论,我们可以称之为逆时针,因为时钟很少向后走。

第一个顶点必须位于路径的外部,这样我们就可以检查其相邻顶点之间的角度以确定绕行顺序。

活动边

在从上到下扫描行时,我们跟踪一个活动边列表。如果一条边穿过当前行,它就会被添加到列表中,否则,它会被移除。由于水平边无法穿过一行,因此可以安全地忽略它们。

对于每一行,列表中的顶点与其顶部和底部进行比较。如果新顶点在底部之上,就会添加一个或多个边。当新顶点在顶部之上时,现有的边就会被移除。

           v0
           /\
          /  \ (a)
         /    \      v2
    (b) /      +-----+
       /      v1      \
      /                \ (c)
     /                  \
    +--------------------+
     v3                  v4

示例

  • v0开始,添加边(a)(b)
  • 扫描直到当前行的底部低于v1 / v2
  • 添加边(c)
  • 扫描直到行顶低于v1
  • 移除边(a)
  • 扫描直到行顶低于v3 / v4
  • 移除边(b)(c)

有符号面积

活动边用于在任意点找到有符号面积。计算达到路径外部的边交叉次数。例如

    +------+
    |      |
    |  +1  |
    |      |
    +------+

一个自相交多边形看起来像这样

    +-----------------+
    |                 |
    |      +------+   |
    |      |       \ /
    |  +1  |  +2    X
    |      |       / \
    |      +------+   |
    |                 |
    +-----------------+

关于绕行顺序相反的子路径怎么办?在这种情况下,应减去而不是加上。

    +----------------+
    |      +-----+   |
    |      |     |   |
    |  +1  |  0  |   |
    |      |     |   |
    |      +-----+   |
    |        <--     |
    |                |
    +----------------+
          -->

扫描行

在扫描行时,对每个像素的符号面积进行采样。从上到下每个边的方向决定了它是对面积进行添加还是减去。在正常绕行顺序中,它添加到面积中;否则,它减去。

此行宽4像素

      -  -  - | -  -  -   -  -  - | -  -  -
    |         |         |         |         |
              | +1                | -1
    |         |         |         |         |
              |                   |      
    | -  -  - | -  -  - | -  -  - | -  -  - |
          0         1         1         0

这些值的累积和是每个像素的有符号面积。

抗锯齿

有时边不会落在像素边界上。在这种情况下,可以使用分数数字来进行抗锯齿。

      -  -  - | -  |  -   -  -  -   -  -  -
    |         |    |    |         |         |
              | +1 | -½   -½
    |         |    |    |         |         |
              |    |                     
    | -  -  - | -  |  - | -  -  - | -  -  - |
          0         ½         0         0

注意第二个边覆盖的余数是如何添加到右侧像素(第三个像素)的。这是必要的,以保持累积和正确。

      -  -  - | -  \  -   -  -  -   -  -  -
    |         |     \   |         |         |
              | +1   \-¼  -¾
    |         |       \ |         |         |
              |        \
    | -  -  - | -  -  - \ -  -  - | -  -  - |
          0         ¾         0         0

合成

可以使用源颜色将符号面积缓冲区与光栅进行合成。

依赖项

~395KB