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 图像
94 每月下载量
在 6 个crate(直接使用 4 个) 中使用
77KB
2K SLoC
footile
一个用 Rust 编写的 2D 矢量图形库。它使用 pix 来处理底层的光栅图像。
请参阅 文档 了解示例和 API 使用方法。
光栅化:鸟瞰图
这里没有新意——这只是一个代码指南。
所以,我们有一个由线、贝塞尔样条、弧等组成的 2D 路径,我们想要从它中制作一个高质量的栅格图像。但是如何实现呢?
模块
path
:定义路径操作和Path2D
geom
:由plotter
和stroker
使用的点(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