Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

177
vendor/zeno/src/command.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
//! Path commands.
use super::geometry::{Point, Transform};
use super::path_builder::PathBuilder;
use core::borrow::Borrow;
/// Path command.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Command {
/// Begins a new subpath at the specified point.
MoveTo(Point),
/// A straight line from the previous point to the specified point.
LineTo(Point),
/// A cubic bezier curve from the previous point to the final point with
/// two intermediate control points.
CurveTo(Point, Point, Point),
/// A quadratic curve from the previous point to the final point with one
/// intermediate control point.
QuadTo(Point, Point),
/// Closes a subpath, connecting the final point to the initial point.
Close,
}
impl Command {
/// Returns the associated verb for the command.
pub fn verb(&self) -> Verb {
use Command::*;
match self {
MoveTo(..) => Verb::MoveTo,
LineTo(..) => Verb::LineTo,
QuadTo(..) => Verb::QuadTo,
CurveTo(..) => Verb::CurveTo,
Close => Verb::CurveTo,
}
}
/// Returns the result of a transformation matrix applied to the command.
#[inline]
pub fn transform(&self, transform: &Transform) -> Self {
use Command::*;
let t = transform;
match self {
MoveTo(p) => MoveTo(t.transform_point(*p)),
LineTo(p) => LineTo(t.transform_point(*p)),
QuadTo(c, p) => QuadTo(t.transform_point(*c), t.transform_point(*p)),
CurveTo(c1, c2, p) => CurveTo(
t.transform_point(*c1),
t.transform_point(*c2),
t.transform_point(*p),
),
Close => Close,
}
}
}
/// Action of a path command.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Verb {
MoveTo,
LineTo,
CurveTo,
QuadTo,
Close,
}
#[derive(Clone)]
pub struct PointsCommands<'a> {
points: &'a [Point],
verbs: &'a [Verb],
point: usize,
verb: usize,
}
impl<'a> PointsCommands<'a> {
pub(super) fn new(points: &'a [Point], verbs: &'a [Verb]) -> Self {
Self {
points,
verbs,
point: 0,
verb: 0,
}
}
#[inline(always)]
pub(super) fn copy_to(&self, sink: &mut impl PathBuilder) {
self.copy_to_inner(sink);
}
#[inline(always)]
fn copy_to_inner(&self, sink: &mut impl PathBuilder) -> Option<()> {
let mut i = 0;
for verb in self.verbs {
match verb {
Verb::MoveTo => {
let p = self.points.get(i)?;
i += 1;
sink.move_to(*p);
}
Verb::LineTo => {
let p = self.points.get(i)?;
i += 1;
sink.line_to(*p);
}
Verb::QuadTo => {
let p = self.points.get(i + 1)?;
let c = self.points.get(i)?;
i += 2;
sink.quad_to(*c, *p);
}
Verb::CurveTo => {
let p = self.points.get(i + 2)?;
let c2 = self.points.get(i + 1)?;
let c1 = self.points.get(i)?;
i += 3;
sink.curve_to(*c1, *c2, *p);
}
Verb::Close => {
sink.close();
}
}
}
Some(())
}
}
impl Iterator for PointsCommands<'_> {
type Item = Command;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
use Command::*;
let verb = self.verbs.get(self.verb)?;
self.verb += 1;
Some(match verb {
Verb::MoveTo => {
let p = self.points.get(self.point)?;
self.point += 1;
MoveTo(*p)
}
Verb::LineTo => {
let p = self.points.get(self.point)?;
self.point += 1;
LineTo(*p)
}
Verb::QuadTo => {
let p = self.points.get(self.point..self.point + 2)?;
self.point += 2;
QuadTo(p[0], p[1])
}
Verb::CurveTo => {
let p = self.points.get(self.point..self.point + 3)?;
self.point += 3;
CurveTo(p[0], p[1], p[2])
}
Verb::Close => Close,
})
}
}
#[derive(Clone)]
pub struct TransformCommands<D> {
pub data: D,
pub transform: Transform,
}
impl<D> Iterator for TransformCommands<D>
where
D: Iterator + Clone,
D::Item: Borrow<Command>,
{
type Item = Command;
fn next(&mut self) -> Option<Self::Item> {
Some(self.data.next()?.borrow().transform(&self.transform))
}
}

602
vendor/zeno/src/geometry.rs vendored Normal file
View File

@@ -0,0 +1,602 @@
//! Geometric primitives.
#[allow(unused)]
use crate::F32Ext;
use core::borrow::Borrow;
use core::ops::{Add, Div, Mul, Sub};
/// Represents an angle in degrees or radians.
#[derive(Copy, Clone, PartialEq, Default, Debug)]
pub struct Angle(f32);
impl Angle {
/// Angle of zero degrees.
pub const ZERO: Self = Self(0.);
/// Creates a new angle from degrees.
pub fn from_degrees(degrees: f32) -> Self {
Self(degrees * core::f32::consts::PI / 180.)
}
/// Creates a new angle from radians.
pub fn from_radians(radians: f32) -> Self {
Self(radians)
}
/// Creates a new angle from gradians.
pub fn from_gradians(gradians: f32) -> Self {
Self::from_degrees(gradians / 400. * 360.)
}
/// Creates a new angle from turns.
pub fn from_turns(turns: f32) -> Self {
Self::from_degrees(turns * 360.)
}
/// Returns the angle in radians.
pub fn to_radians(self) -> f32 {
self.0
}
/// Returns the angle in degrees.
pub fn to_degrees(self) -> f32 {
self.0 * 180. / core::f32::consts::PI
}
}
/// Two dimensional vector.
#[derive(Copy, Clone, PartialEq, Default, Debug)]
pub struct Vector {
pub x: f32,
pub y: f32,
}
impl Vector {
/// Vector with both components set to zero.
pub const ZERO: Self = Self { x: 0., y: 0. };
/// Creates a new vector with the specified coordinates.
#[inline]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
/// Returns the length of the vector.
#[inline]
pub fn length(self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
/// Returns the squared length of the vector.
#[inline]
pub fn length_squared(self) -> f32 {
self.x * self.x + self.y * self.y
}
/// Returns the distance between two points.
#[inline]
pub fn distance_to(self, other: Self) -> f32 {
(self - other).length()
}
/// Computes the dot product of two vectors.
#[inline]
pub fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y
}
/// Computes the cross product of two vectors.
#[inline]
pub fn cross(self, other: Self) -> f32 {
self.x * other.y - self.y * other.x
}
/// Returns a normalized copy of the vector.
#[inline]
pub fn normalize(self) -> Self {
let length = self.length();
if length == 0. {
return Self::new(0., 0.);
}
let inverse = 1. / length;
Self::new(self.x * inverse, self.y * inverse)
}
/// Returns a new vector containing the smallest integer values greater than
/// or equal to each component.
pub fn ceil(self) -> Self {
Self::new(self.x.ceil(), self.y.ceil())
}
/// Returns a new vector containing the largest integer values less than
/// or equal to each component.
pub fn floor(self) -> Self {
Self::new(self.x.floor(), self.y.floor())
}
/// Returns the angle to the specified vector.
pub fn angle_to(self, other: Self) -> Angle {
Angle::from_radians(self.cross(other).atan2(self.dot(other)))
}
/// Returns true if this vector is approximately equal to other using a
/// standard single precision epsilon value.
#[inline]
pub fn nearly_eq(self, other: Vector) -> bool {
self.nearly_eq_by(other, f32::EPSILON)
}
/// Returns true if this vector is approximately equal to other using
/// the specified epsilon value.
#[inline]
pub fn nearly_eq_by(self, other: Vector, epsilon: f32) -> bool {
(self.x - other.x).abs() < epsilon && (self.y - other.y).abs() < epsilon
}
}
impl Add for Vector {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl Sub for Vector {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self::new(self.x - rhs.x, self.y - rhs.y)
}
}
impl Mul for Vector {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self::new(self.x * rhs.x, self.y * rhs.y)
}
}
impl Mul<f32> for Vector {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self {
Self::new(self.x * rhs, self.y * rhs)
}
}
impl Div for Vector {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
Self::new(self.x / rhs.x, self.y / rhs.y)
}
}
impl Div<f32> for Vector {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self {
let s = 1. / rhs;
Self::new(self.x * s, self.y * s)
}
}
impl From<[f32; 2]> for Vector {
fn from(v: [f32; 2]) -> Self {
Self::new(v[0], v[1])
}
}
impl From<[i32; 2]> for Vector {
fn from(v: [i32; 2]) -> Self {
Self::new(v[0] as f32, v[1] as f32)
}
}
impl From<(f32, f32)> for Vector {
fn from(v: (f32, f32)) -> Self {
Self::new(v.0, v.1)
}
}
impl From<(i32, i32)> for Vector {
fn from(v: (i32, i32)) -> Self {
Self::new(v.0 as f32, v.1 as f32)
}
}
impl From<(f32, i32)> for Vector {
fn from(v: (f32, i32)) -> Self {
Self::new(v.0, v.1 as f32)
}
}
impl From<(i32, f32)> for Vector {
fn from(v: (i32, f32)) -> Self {
Self::new(v.0 as f32, v.1)
}
}
impl From<f32> for Vector {
fn from(x: f32) -> Self {
Self::new(x, x)
}
}
impl From<i32> for Vector {
fn from(x: i32) -> Self {
let x = x as f32;
Self::new(x, x)
}
}
impl From<Vector> for [f32; 2] {
fn from(v: Vector) -> Self {
[v.x, v.y]
}
}
impl From<Vector> for (f32, f32) {
fn from(v: Vector) -> Self {
(v.x, v.y)
}
}
/// Alias for vector to distinguish intended use.
pub type Point = Vector;
#[inline(always)]
pub(super) fn normal(start: Vector, end: Vector) -> Vector {
Vector::new(end.y - start.y, -(end.x - start.x)).normalize()
}
/// Two dimensional transformation matrix.
#[derive(Copy, Clone, Default, Debug)]
pub struct Transform {
pub xx: f32,
pub xy: f32,
pub yx: f32,
pub yy: f32,
pub x: f32,
pub y: f32,
}
impl Transform {
/// Identity matrix.
pub const IDENTITY: Self = Self {
xx: 1.,
xy: 0.,
yy: 1.,
yx: 0.,
x: 0.,
y: 0.,
};
/// Creates a new transform.
pub fn new(xx: f32, xy: f32, yx: f32, yy: f32, x: f32, y: f32) -> Self {
Self {
xx,
xy,
yx,
yy,
x,
y,
}
}
/// Creates a translation transform.
pub fn translation(x: f32, y: f32) -> Self {
Self::new(1., 0., 0., 1., x, y)
}
/// Creates a rotation transform.
pub fn rotation(angle: Angle) -> Self {
let (sin, cos) = angle.0.sin_cos();
Self {
xx: cos,
xy: sin,
yx: -sin,
yy: cos,
x: 0.,
y: 0.,
}
}
/// Creates a rotation transform around a point.
pub fn rotation_about(point: impl Into<Point>, angle: Angle) -> Self {
let p = point.into();
Self::translation(p.x, p.y)
.then_rotate(angle)
.then_translate(-p.x, -p.y)
}
/// Creates a scale transform.
pub fn scale(x: f32, y: f32) -> Self {
Self::new(x, 0., 0., y, 0., 0.)
}
/// Creates a skew transform.
pub fn skew(x: Angle, y: Angle) -> Self {
Self {
xx: 1.,
xy: y.0.tan(),
yx: x.0.tan(),
yy: 1.,
x: 0.,
y: 0.,
}
}
fn combine(a: &Transform, b: &Transform) -> Self {
let xx = a.xx * b.xx + a.yx * b.xy;
let yx = a.xx * b.yx + a.yx * b.yy;
let xy = a.xy * b.xx + a.yy * b.xy;
let yy = a.xy * b.yx + a.yy * b.yy;
let x = a.x * b.xx + a.y * b.xy + b.x;
let y = a.x * b.yx + a.y * b.yy + b.y;
Self {
xx,
yx,
xy,
yy,
x,
y,
}
}
/// Returns a new transform that represents the application of this transform
/// followed by other.
pub fn then(&self, other: &Transform) -> Self {
Self::combine(self, other)
}
/// Returns a new transform that represents a translation followed by this
/// transform.
pub fn pre_translate(&self, x: f32, y: f32) -> Self {
Self::combine(&Self::translation(x, y), self)
}
/// Returns a new transform that represents this transform followed by a
/// translation.
pub fn then_translate(&self, x: f32, y: f32) -> Self {
let mut t = *self;
t.x += x;
t.y += y;
t
}
/// Returns a new transform that represents a rotation followed by this
/// transform.
pub fn pre_rotate(&self, angle: Angle) -> Self {
Self::combine(&Self::rotation(angle), self)
}
/// Returns a new transform that represents this transform followed by a
/// rotation.
pub fn then_rotate(&self, angle: Angle) -> Self {
Self::combine(self, &Self::rotation(angle))
}
/// Returns a new transform that represents a scale followed by this
/// transform.
pub fn pre_scale(&self, x: f32, y: f32) -> Self {
Self::combine(&Self::scale(x, y), self)
}
/// Returns a new transform that represents this transform followed by a
/// scale.
pub fn then_scale(&self, x: f32, y: f32) -> Self {
Self::combine(self, &Self::scale(x, y))
}
/// Returns the determinant of the transform.
pub fn determinant(&self) -> f32 {
self.xx * self.yy - self.yx * self.xy
}
/// Returns the inverse of the transform, if any.
pub fn invert(&self) -> Option<Transform> {
let det = self.determinant();
if !det.is_finite() || det == 0. {
return None;
}
let s = 1. / det;
let a = self.xx;
let b = self.xy;
let c = self.yx;
let d = self.yy;
let x = self.x;
let y = self.y;
Some(Transform {
xx: d * s,
xy: -b * s,
yx: -c * s,
yy: a * s,
x: (b * y - d * x) * s,
y: (c * x - a * y) * s,
})
}
/// Returns the result of applying this transform to a point.
#[inline(always)]
pub fn transform_point(&self, point: Point) -> Point {
Vector {
x: (point.x * self.xx + point.y * self.yx) + self.x,
y: (point.x * self.xy + point.y * self.yy) + self.y,
}
}
/// Returns the result of applying this transform to a vector.
#[inline(always)]
pub fn transform_vector(&self, vector: Vector) -> Vector {
Vector {
x: (vector.x * self.xx + vector.y * self.yx),
y: (vector.x * self.xy + vector.y * self.yy),
}
}
}
/// The origin of the coordinate system for rendering.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Origin {
/// Origin (0, 0) at the top left of the image.
TopLeft,
/// Origin (0, 0) at the bottom left of the image.
BottomLeft,
}
impl Default for Origin {
fn default() -> Self {
Self::TopLeft
}
}
/// Describes the offset and dimensions of a rendered mask.
#[derive(Copy, Clone, Debug, Default)]
pub struct Placement {
/// Horizontal offset with respect to the origin specified when computing
/// the placement.
pub left: i32,
/// Vertical offset with respect to the origin specified when computing
/// the placement.
pub top: i32,
/// Width in pixels.
pub width: u32,
/// Height in pixels.
pub height: u32,
}
impl Placement {
/// Given an origin, offset and bounding box, computes the resulting offset
/// and placement for a tightly bounded mask.
pub fn compute(
origin: Origin,
offset: impl Into<Vector>,
bounds: &Bounds,
) -> (Vector, Placement) {
let offset = offset.into();
let mut bounds = *bounds;
bounds.min = (bounds.min + offset).floor();
bounds.max = (bounds.max + offset).ceil();
let offset = Vector::new(-bounds.min.x, -bounds.min.y);
let width = bounds.width() as u32;
let height = bounds.height() as u32;
let left = -offset.x as i32;
let top = if origin == Origin::BottomLeft {
(-offset.y).floor() + height as f32
} else {
-offset.y
} as i32;
(
offset,
Placement {
left,
top,
width,
height,
},
)
}
}
/// Axis-aligned bounding box.
#[derive(Copy, Clone, Default, Debug)]
pub struct Bounds {
pub min: Point,
pub max: Point,
}
impl Bounds {
/// Creates a new bounding box from minimum and maximum points.
pub fn new(min: Point, max: Point) -> Self {
Self { min, max }
}
/// Creates a new bounding box from a sequence of points.
pub fn from_points<I>(points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Point>,
{
let mut b = BoundsBuilder::new();
for p in points {
b.add(*p.borrow());
}
b.build()
}
/// Returns true if the bounding box is empty.
pub fn is_empty(&self) -> bool {
self.min.x >= self.max.x || self.min.y >= self.max.y
}
/// Returns the width of the bounding box.
pub fn width(&self) -> f32 {
self.max.x - self.min.x
}
/// Returns the height of the bounding box.
pub fn height(&self) -> f32 {
self.max.y - self.min.y
}
/// Returns true if the box contains the specified point.
pub fn contains(&self, point: impl Into<Point>) -> bool {
let p = point.into();
p.x > self.min.x && p.x < self.max.x && p.y > self.min.y && p.y < self.max.y
}
}
pub(super) struct BoundsBuilder {
pub count: usize,
#[allow(dead_code)]
pub start: Point,
pub current: Point,
pub min: Point,
pub max: Point,
}
impl BoundsBuilder {
pub fn new() -> Self {
Self {
count: 0,
start: Point::ZERO,
current: Point::ZERO,
min: Point::new(f32::MAX, f32::MAX),
max: Point::new(f32::MIN, f32::MIN),
}
}
pub fn add(&mut self, p: Point) -> &mut Self {
let x = p.x;
let y = p.y;
if x < self.min.x {
self.min.x = x;
}
if x > self.max.x {
self.max.x = x;
}
if y < self.min.y {
self.min.y = y;
}
if y > self.max.y {
self.max.y = y;
}
self.count += 1;
self
}
pub fn build(&self) -> Bounds {
if self.count != 0 {
Bounds {
min: self.min,
max: self.max,
}
} else {
Bounds::default()
}
}
}

90
vendor/zeno/src/hit_test.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
//! Hit testing.
use super::geometry::{Point, Transform};
use super::mask::Mask;
use super::path_data::PathData;
use super::scratch::Scratch;
use super::style::{Fill, Style};
use core::cell::RefCell;
/// Builder for configuring and executing a hit test.
pub struct HitTest<'a, 's, D> {
data: D,
style: Style<'a>,
transform: Option<Transform>,
threshold: u8,
scratch: RefCell<Option<&'s mut Scratch>>,
}
impl<'a, 's, D> HitTest<'a, 's, D>
where
D: PathData,
{
/// Creates a new hit test builder for the specified path data.
pub fn new(data: D) -> Self {
Self {
data,
style: Style::Fill(Fill::NonZero),
transform: None,
threshold: 0,
scratch: RefCell::new(None),
}
}
/// Creates a new hit test builder for the specified path data and scratch memory.
pub fn with_scratch(data: D, scratch: &'s mut Scratch) -> Self {
Self {
data,
style: Style::Fill(Fill::NonZero),
transform: None,
threshold: 0,
scratch: RefCell::new(Some(scratch)),
}
}
/// Sets the style of the path.
pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
self.style = style.into();
self
}
/// Sets the transformation matrix of the path.
pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
self.transform = transform;
self
}
/// Sets the threshold value for determining whether a hit test registers.
pub fn threshold(&mut self, threshold: u8) -> &mut Self {
self.threshold = threshold;
self
}
/// Returns true if the specified point is painted by the path.
pub fn test(&self, point: impl Into<Point>) -> bool {
let mut scratch = self.scratch.borrow_mut();
let mut buf = [0u8; 1];
let p = point.into() * -1.;
if let Some(scratch) = scratch.as_mut() {
Mask::with_scratch(&self.data, scratch)
.style(self.style)
.offset(p)
.transform(self.transform)
.size(1, 1)
.render_into(&mut buf, None);
} else {
Mask::new(&self.data)
.style(self.style)
.offset(p)
.transform(self.transform)
.size(1, 1)
.render_into(&mut buf, None);
}
if self.threshold == 0xFF {
buf[0] >= self.threshold
} else {
buf[0] > self.threshold
}
}
}

382
vendor/zeno/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,382 @@
/*!
This crate provides a high performance, low level 2D rasterization library
with support for rendering paths of various styles into alpha or subpixel
masks.
Broadly speaking, support is provided for the following:
- 256x anti-aliased rasterization (8-bit alpha or 32-bit RGBA subpixel alpha)
- Pixel perfect hit testing with customizable coverage threshold
- Non-zero and even-odd fills
- Stroking with the standard set of joins and caps
(separate start and end caps are possible)
- Numerically stable dashing for smooth dash offset animation
- Vertex traversal for marker placement
- Stepped distance traversal for animation or text-on-path support
- Abstract representation of path data that imposes no policy on storage
While this crate is general purpose, in the interest of interoperability and
familiarity, the feature set was chosen specifically to accommodate the
requirements of the
[SVG path specification](https://www.w3.org/TR/SVG/paths.html).
Furthermore, the rasterized masks are nearly identical to those generated by
Skia (sans slight AA differences) and as such, should yield images that are
equivalent to those produced by modern web browsers.
# Rendering
Due to the large configuration space for styling and rendering paths, the
builder pattern is used pervasively. The [`Mask`] struct is the builder
used for rasterization. For example, to render a simple triangle into a
64x64 8-bit alpha mask:
```rust
use zeno::{Mask, PathData};
// The target buffer that will contain the mask
let mut mask = [0u8; 64 * 64];
// Create a new mask with some path data
Mask::new("M 8,56 32,8 56,56 Z")
// Choose an explicit size for the target
.size(64, 64)
// Finally, render the path into the target
.render_into(&mut mask, None);
```
Note that, in this case, the path itself is supplied as a string in SVG path
data format. This crate provides several different kinds of path data by
default along with support for custom implementations. See the
[`PathData`] trait for more detail.
The previous example did not provide a style, so a non-zero
[`Fill`] was chosen by default. Let's render the same path with
a 4 pixel wide stroke and a round line join:
```rust
use zeno::{Join, Mask, PathData, Stroke};
let mut mask = [0u8; 64 * 64];
Mask::new("M 8,56 32,8 56,56 Z")
.size(64, 64)
.style(Stroke::new(4.0).join(Join::Round))
.render_into(&mut mask, None);
```
Or to make it a bit more dashing:
```rust
use zeno::{Cap, Join, Mask, PathData, Stroke};
let mut mask = [0u8; 64 * 64];
Mask::new("M 8,56 32,8 56,56 Z")
.style(
Stroke::new(4.0)
.join(Join::Round)
.cap(Cap::Round)
// dash accepts a slice of dash lengths and an initial dash offset
.dash(&[10.0, 12.0, 0.0], 0.0),
)
.size(64, 64)
.render_into(&mut mask, None);
```
See the [`Stroke`] builder struct for all available options.
So far, we've generated our masks into fixed buffers with explicit sizes. It is
often the case that it is preferred to ignore all empty space and render a path
into a tightly bound mask of dynamic size. This can be done by eliding the call
for the size method:
```rust
use zeno::{Mask, PathData};
// Dynamic buffer that will contain the mask
let mut mask = Vec::new();
let placement = Mask::new("M 8,56 32,8 56,56 Z")
// Insert an inspect call here to access the computed dimensions
.inspect(|format, width, height| {
// Make sure our buffer is the correct size
mask.resize(format.buffer_size(width, height), 0);
})
.render_into(&mut mask, None);
```
The call to size has been replaced with a call to inspect which injects a
closure into the call chain giving us the opportunity to extend our buffer to
the appropriate size. Note also that the render method has a return value that
has been captured here. This [`Placement`] struct describes the dimensions of
the resulting mask along with an offset that should be applied during
composition to compensate for the removal of any empty space.
Finally, it is possible to render without a target buffer, in which case the
rasterizer will allocate and return a new `Vec<u8>` containing the mask:
```rust
use zeno::{Mask, PathData};
// mask is a Vec<u8>
let (mask, placement) = Mask::new("M 8,56 32,8 56,56 Z")
// Calling render() instead of render_into() will allocate a buffer
// for you that is returned along with the placement
.render();
```
Both [`Mask`] and [`Stroke`] offer large sets of options for fine-grained
control of styling and rasterization including offsets, scaling,
transformations, formats, coordinate spaces and more. See
their respective documentation for more detail.
# Hit testing
Hit testing is the process of determining if a point is within the region that
would be painted by the path. A typical use case is to determine if a user's
cursor is hovering over a particular path. The process generally follows the
same form as rendering:
```rust
use zeno::{HitTest, PathData};
// A 20x10 region with the right half covered by the path
let hit_test = HitTest::new("M10,0 10,10 20,10 20,0 Z");
assert_eq!(hit_test.test([15, 5]), true);
assert_eq!(hit_test.test([5, 5]), false);
```
Due to the fact that paths are anti-aliased, the hit test builder offers a
threshold option that determines how much "coverage" is required for a hit test
to pass at a particular point.
```rust
use zeno::{HitTest, PathData};
let mut hit_test = HitTest::new("M2.5,0 2.5,2 5,2 5,0 Z");
// Require full coverage for a successful hit test
hit_test.threshold(255);
assert_eq!(hit_test.test([2, 0]), false);
// Succeed for any non-zero coverage
hit_test.threshold(0);
assert_eq!(hit_test.test([2, 0]), true);
```
See the [`HitTest`] type for more detail.
# Path building
While SVG paths are a reasonable choice for static storage, there sometimes
arise cases where paths must be built dynamically at runtime:
```rust
use zeno::{Command, Mask, PathBuilder, PathData};
// Create a vector to store the path commands
let mut path: Vec<Command> = Vec::new();
// Construct the path with chained method calls
path.move_to([8, 56]).line_to([32, 8]).line_to([56, 56]).close();
// Ensure it is equal to the equivalent SVG path
assert!((&path).commands().eq("M 8,56 32,8 56,56 Z".commands()));
// &Vec<Command> is also valid path data
Mask::new(&path).render(); // ...
```
Here, a vector of [`Command`]s is used to store the path data and the
[`PathBuilder`] trait provides the extension methods necessary for
building a path.
Beyond the four basic path commands, the path builder trait also provides
arcs (and position relative versions of all previous commands) along with
rectangles, round rectangles, ellipses and circles:
```rust
use zeno::{Angle, ArcSize, ArcSweep, Command, PathBuilder, PathData};
let mut path: Vec<Command> = Vec::new();
path.move_to([1, 2]).rel_arc_to(
8.0,
4.0,
Angle::from_degrees(30.0),
ArcSize::Small,
ArcSweep::Positive,
[10, 4],
);
assert!((&path).commands().eq("M1,2 a8,4,30,0,1,10,4".commands()));
```
Along with incremental building of paths, path builder can also be used as a
"sink" for capturing the result of the application of a style and transform
to some path data. For example, it is possible to store the output of a stroke
style to avoid the cost of stroke evaluation for future rendering or hit test
operations with the use of the [`apply`] function:
```rust
use zeno::{apply, Cap, Command, PathBuilder, PathData, Stroke};
let mut stroke: Vec<Command> = Vec::new();
apply("L10,0", Stroke::new(4.0).cap(Cap::Round), None, &mut stroke);
```
[`PathBuilder`] is only implemented for `Vec<Command>` by default, but
custom implementations are possible to support capturing and building
paths into other data structures.
# Traversal
Path traversal involves incremental evaluation of a path by some metric. This
crate currently provides two methods of traversal.
The [`Vertices`] iterator yields a variant of the [`Vertex`] enum at the
beginning and end of each subpath and between each path command. Each variant
provides all the geometric information necessary to place SVG style markers.
The [`Walk`] type is an iterator-like type that allows for
stepping along the path by arbitrary distances. Each step yields the position
on the path at the next distance along with a vector describing the
left-ward direction from the path at that point. This is useful for animating
objects along a path, or for rendering text attached to a path.
# Transient memory allocations
The algorithms in this crate make a concerted effort to avoid dynamic
allocations where possible, but paths of significant size or complexity
may cause spills into temporary heap memory. Specifically, stroke evaluation
and rasterization may cause heap allocations.
To amortize the cost of these, the appropriately named
[`Scratch`] struct is available. This type contains internal
heap allocated storage and provides replacement methods for functions that may
allocate. In addition, the [`Mask::with_scratch`] and [`HitTest::with_scratch`]
constructors are provided which take a scratch instance as an argument and
redirect all transient allocations to the reusable storage.
*/
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(any(feature = "std", feature = "libm")))]
compile_error! { "Either the std or libm feature must be enabled" }
extern crate alloc;
mod command;
mod geometry;
#[cfg(feature = "eval")]
mod hit_test;
#[cfg(feature = "eval")]
mod mask;
mod path_builder;
mod path_data;
#[cfg(feature = "eval")]
mod raster;
#[cfg(feature = "eval")]
mod scratch;
mod segment;
#[cfg(feature = "eval")]
mod stroke;
mod style;
mod svg_parser;
#[cfg(feature = "eval")]
mod traversal;
pub use command::{Command, Verb};
pub use geometry::{Angle, Bounds, Origin, Placement, Point, Transform, Vector};
#[cfg(feature = "eval")]
pub use hit_test::HitTest;
#[cfg(feature = "eval")]
pub use mask::{Format, Mask};
pub use path_builder::{ArcSize, ArcSweep, PathBuilder};
#[cfg(feature = "eval")]
pub use path_data::{apply, bounds};
pub use path_data::{length, PathData};
#[cfg(feature = "eval")]
pub use scratch::Scratch;
pub use style::*;
pub use svg_parser::validate_svg;
#[cfg(feature = "eval")]
pub use traversal::{Vertex, Vertices, Walk};
macro_rules! define_f32_ext {
($($fpname:ident($($argname:ident: $argty:ty),*) -> $ret:ty => $libmname:ident;)*) => {
/// An extension trait defining floating point operations.
#[allow(dead_code)]
trait F32Ext {
$(
fn $fpname(self, $($argname:$argty),*) -> $ret;
)*
}
#[cfg(feature = "std")]
impl F32Ext for f32 {
$(
fn $fpname(self, $($argname:$argty),*) -> $ret {
// This intrinsic is natively defined in libstd.
f32::$fpname(self, $($argname),*)
}
)*
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
impl F32Ext for f32 {
$(
fn $fpname(self, $($argname:$argty),*) -> $ret {
// Use the libm version of this intrinsic.
<$ret>::libm_cvt(libm::$libmname(
self.into(),
$(($argname).into()),*
) as _)
}
)*
}
}
}
define_f32_ext! {
abs() -> f32 => fabs;
acos() -> f32 => acos;
atan2(x:f32) -> f32 => atan2;
ceil() -> f32 => ceil;
cos() -> f32 => cos;
floor() -> f32 => floor;
sin_cos() -> (f32, f32) => sincos;
sqrt() -> f32 => sqrt;
powf(x:f32) -> f32 => powf;
powi(x:i32) -> f32 => pow;
tan() -> f32 => tan;
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
trait LibmCvt {
type Input;
fn libm_cvt(input: Self::Input) -> Self;
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
impl LibmCvt for f32 {
type Input = f64;
fn libm_cvt(input: f64) -> f32 {
input as f32
}
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
impl LibmCvt for (f32, f32) {
type Input = (f64, f64);
fn libm_cvt((a, b): (f64, f64)) -> (f32, f32) {
(a as f32, b as f32)
}
}
// Prep for no_std support when core supports FP intrinsics.
mod lib {
pub use alloc::vec::Vec;
}

455
vendor/zeno/src/mask.rs vendored Normal file
View File

@@ -0,0 +1,455 @@
//! Mask generator.
use super::geometry::{Origin, Placement, Transform, Vector};
use super::path_data::{apply, PathData};
use super::scratch::Scratch;
use super::style::{Fill, Style};
#[allow(unused)]
use super::F32Ext;
use crate::lib::Vec;
use core::cell::RefCell;
/// The desired output image format for rendering.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Format {
/// 8-bit alpha mask.
Alpha,
/// 32-bit RGBA subpixel mask with 1/3 pixel offsets for the red and
/// blue channels.
Subpixel,
/// 32-bit RGBA subpixel mask with custom offsets.
CustomSubpixel([f32; 3]),
}
impl Format {
/// Creates a format for BGRA subpixel rendering.
pub fn subpixel_bgra() -> Self {
Self::CustomSubpixel([0.3, 0., -0.3])
}
/// Returns the necessary buffer size to hold an image of the specified
/// width and height with this format.
pub fn buffer_size(self, width: u32, height: u32) -> usize {
(width
* height
* match self {
Self::Alpha => 1,
_ => 4,
}) as usize
}
}
impl Default for Format {
fn default() -> Self {
Self::Alpha
}
}
/// Builder for configuring and rendering a mask.
pub struct Mask<'a, 's, D> {
data: D,
style: Style<'a>,
transform: Option<Transform>,
format: Format,
origin: Origin,
offset: Vector,
render_offset: Vector,
width: u32,
height: u32,
explicit_size: bool,
has_size: bool,
bounds_offset: Vector,
scratch: RefCell<Option<&'s mut Scratch>>,
}
impl<'a, 's, D> Mask<'a, 's, D>
where
D: PathData,
{
/// Creates a new mask builder for the specified path data.
pub fn new(data: D) -> Self {
Self {
data,
style: Style::Fill(Fill::NonZero),
transform: None,
format: Format::Alpha,
origin: Origin::TopLeft,
offset: Vector::ZERO,
render_offset: Vector::ZERO,
width: 0,
height: 0,
explicit_size: false,
has_size: false,
bounds_offset: Vector::ZERO,
scratch: RefCell::new(None),
}
}
/// Creates a new mask builder for the specified path data and scratch memory.
pub fn with_scratch(data: D, scratch: &'s mut Scratch) -> Self {
Self {
data,
style: Style::Fill(Fill::NonZero),
transform: None,
format: Format::Alpha,
origin: Origin::TopLeft,
offset: Vector::ZERO,
render_offset: Vector::ZERO,
width: 0,
height: 0,
explicit_size: false,
has_size: false,
bounds_offset: Vector::ZERO,
scratch: RefCell::new(Some(scratch)),
}
}
/// Sets the style of the path. The default is a non-zero fill.
pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
self.style = style.into();
self
}
/// Sets the transformation matrix of the path.
pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
self.transform = transform;
self
}
/// Sets the desired format of the mask. The default value is an 8-bit
/// alpha format.
pub fn format(&mut self, format: Format) -> &mut Self {
self.format = format;
self
}
/// Sets the origin that defines the coordinate system for the mask.
pub fn origin(&mut self, origin: Origin) -> &mut Self {
self.origin = origin;
self
}
/// Sets the offset for the path's rendered bounds. To translate both the
/// path and its rendered bounding box, set both [`Self::offset`] and
/// [`Self::render_offset`].
pub fn offset(&mut self, offset: impl Into<Vector>) -> &mut Self {
self.offset = offset.into();
self
}
/// Sets an explicit size for the mask. If left unspecified, the size will
/// be computed from the bounding box of the path after applying any
/// relevant style, offset and transform.
pub fn size(&mut self, width: u32, height: u32) -> &mut Self {
self.width = width;
self.height = height;
self.explicit_size = true;
self.has_size = true;
self
}
/// Sets an additional rendering offset for the mask. This offset does not
/// affect bounds or size computations and is only applied during
/// rendering.
pub fn render_offset(&mut self, offset: impl Into<Vector>) -> &mut Self {
self.render_offset = offset.into();
self
}
/// Invokes a closure with the format, width and height of the mask provided
/// as arguments. This is primarily useful for preparing a target buffer without
/// interrupting the call chain.
pub fn inspect(&mut self, mut f: impl FnMut(Format, u32, u32)) -> &mut Self {
self.ensure_size();
f(self.format, self.width, self.height);
self
}
/// Renders the mask into a byte buffer. If specified, the pitch describes
/// the number of bytes between subsequent rows of the target buffer. This
/// is primarily useful for rendering into tiled images such as texture
/// atlases. If left unspecified, the buffer is assumed to be linear and
/// tightly packed.
pub fn render_into(&self, buffer: &mut [u8], pitch: Option<usize>) -> Placement {
let (offset, placement) = self.placement();
let pitch = match pitch {
Some(pitch) => pitch,
_ => {
placement.width as usize
* match self.format {
Format::Alpha => 1,
_ => 4,
}
}
};
render(self, offset, &placement, buffer, pitch);
placement
}
/// Renders the mask to a newly allocated buffer.
pub fn render(&self) -> (Vec<u8>, Placement) {
let mut buf = Vec::new();
let (offset, placement) = self.placement();
buf.resize(
self.format.buffer_size(placement.width, placement.height),
0,
);
let pitch = placement.width as usize
* match self.format {
Format::Alpha => 1,
_ => 4,
};
render(self, offset, &placement, &mut buf, pitch);
(buf, placement)
}
fn ensure_size(&mut self) {
if self.has_size {
return;
}
let (offset, placement) = self.placement();
self.bounds_offset = offset;
self.width = placement.width;
self.height = placement.height;
self.explicit_size = false;
self.has_size = true;
}
fn placement(&self) -> (Vector, Placement) {
let mut placement = Placement {
left: 0,
top: 0,
width: self.width,
height: self.height,
};
let mut offset = self.offset;
if self.explicit_size {
return (offset, placement);
} else if !self.has_size {
let mut scratch = self.scratch.borrow_mut();
let mut bounds = if let Some(scratch) = scratch.as_mut() {
scratch.bounds(&self.data, self.style, self.transform)
} else {
super::bounds(&self.data, self.style, self.transform)
};
bounds.min = (bounds.min + self.offset).floor();
bounds.max = (bounds.max + self.offset).ceil();
offset = Vector::new(-bounds.min.x, -bounds.min.y);
placement.width = bounds.width() as u32;
placement.height = bounds.height() as u32;
} else {
offset = self.bounds_offset;
}
placement.left = -offset.x as i32;
placement.top = if self.origin == Origin::BottomLeft {
(-offset.y).floor() + self.height as f32
} else {
-offset.y
} as i32;
(offset, placement)
}
}
#[allow(clippy::needless_lifetimes)]
pub fn render<'a, 'c, D>(
mask: &'a Mask<'a, 'c, D>,
offset: Vector,
placement: &Placement,
buf: &mut [u8],
pitch: usize,
) where
D: PathData,
{
let y_up = mask.origin == Origin::BottomLeft;
let (is_subpx, subpx) = match mask.format {
Format::Alpha => (false, [Vector::ZERO; 3]),
Format::Subpixel => (
true,
[Vector::new(-0.3, 0.), Vector::ZERO, Vector::new(0.3, 0.)],
),
Format::CustomSubpixel(subpx) => (
true,
[
Vector::new(subpx[0], 0.),
Vector::new(subpx[1], 0.),
Vector::new(subpx[2], 0.),
],
),
};
let fill = match mask.style {
Style::Fill(fill) => fill,
_ => Fill::NonZero,
};
let w = placement.width;
let h = placement.height;
let shift = offset + mask.render_offset;
let data = &mask.data;
let style = mask.style;
let transform = mask.transform;
let mut scratch = mask.scratch.borrow_mut();
use super::raster::{AdaptiveStorage, Rasterizer};
if let Some(scratch) = scratch.as_mut() {
let mut ras = Rasterizer::new(&mut scratch.render);
let inner = &mut scratch.inner;
if is_subpx {
ras.rasterize_write(
shift + subpx[0],
w,
h,
&mut |r| {
inner.apply(data, &style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
ras.rasterize_write(
shift + subpx[1],
w,
h,
&mut |r| {
inner.apply(data, &style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4 + 1;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
ras.rasterize_write(
shift + subpx[2],
w,
h,
&mut |r| {
inner.apply(data, &style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4 + 2;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
} else {
ras.rasterize(
shift,
w,
h,
&mut |r| {
inner.apply(data, &style, transform, r);
},
fill,
buf,
pitch,
y_up,
);
}
} else {
let mut storage = AdaptiveStorage::new();
let mut ras = Rasterizer::new(&mut storage);
if is_subpx {
ras.rasterize_write(
shift + subpx[0],
w,
h,
&mut |r| {
apply(data, style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
ras.rasterize_write(
shift + subpx[1],
w,
h,
&mut |r| {
apply(data, style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4 + 1;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
ras.rasterize_write(
shift + subpx[2],
w,
h,
&mut |r| {
apply(data, style, transform, r);
},
fill,
pitch,
y_up,
&mut |row_offset, x, count, coverage| {
let buf = &mut buf[row_offset..];
let mut i = 0;
let mut j = x * 4 + 2;
while i < count {
buf[j] = coverage;
i += 1;
j += 4;
}
},
);
} else {
ras.rasterize(
shift,
w,
h,
&mut |r| {
apply(data, style, transform, r);
},
fill,
buf,
pitch,
y_up,
);
}
}
}

593
vendor/zeno/src/path_builder.rs vendored Normal file
View File

@@ -0,0 +1,593 @@
//! Path builder.
#![allow(clippy::excessive_precision)]
use super::command::Command;
use super::geometry::{Angle, BoundsBuilder, Point, Transform};
#[allow(unused)]
use super::F32Ext;
use crate::lib::Vec;
use core::f32;
/// Describes the size of an arc.
#[derive(Copy, Clone, PartialEq)]
pub enum ArcSize {
/// An arc of <= 180 degrees will be drawn.
Small,
/// An arc of >= 180 degrees will be drawn.
Large,
}
/// Describes the sweep direction for an arc.
#[derive(Copy, Clone, PartialEq)]
pub enum ArcSweep {
/// The arc is drawn in a positive angle direction.
Positive,
/// The arc is drawn in a negative angle direction.
Negative,
}
/// Trait for types that accept path commands.
pub trait PathBuilder: Sized {
/// Returns the current point of the path.
fn current_point(&self) -> Point;
/// Moves to the specified point, beginning a new subpath.
fn move_to(&mut self, to: impl Into<Point>) -> &mut Self;
/// Moves to the specified point, relative to the current point,
/// beginning a new subpath.
fn rel_move_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.move_to(to.into() + self.current_point())
}
/// Adds a line to the specified point. This will begin a new subpath
/// if the path is empty or the previous subpath was closed.
fn line_to(&mut self, to: impl Into<Point>) -> &mut Self;
/// Adds a line to the specified point, relative to the current point. This
/// will begin a new subpath if the path is empty or the previous subpath
/// was closed.
fn rel_line_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.line_to(to.into() + self.current_point())
}
/// Adds a cubic bezier curve from the current point through the specified
/// control points to the final point. This will begin a new subpath if the
/// path is empty or the previous subpath was closed.
fn curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self;
/// Adds a cubic bezier curve from the current point through the specified
/// control points to the final point. All points are considered relative to the
/// current point. This will begin a new subpath if the path is empty or the
/// previous subpath was closed.
fn rel_curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self {
let r = self.current_point();
self.curve_to(control1.into() + r, control2.into() + r, to.into() + r)
}
/// Adds a quadratic bezier curve from the current point through the specified
/// control point to the final point. This will begin a new subpath if the
/// path is empty or the previous subpath was closed.
fn quad_to(&mut self, control1: impl Into<Point>, to: impl Into<Point>) -> &mut Self;
/// Adds a quadratic bezier curve from the current point through the specified
/// control point to the final point. All points are considered relative to the
/// current point. This will begin a new subpath if the path is empty or the
/// previous subpath was closed.
fn rel_quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
let r = self.current_point();
self.quad_to(control.into() + r, to.into() + r)
}
/// Adds an arc with the specified x- and y-radius, rotation angle, arc size,
/// and arc sweep from the current point to the specified end point. The center
/// point of the arc will be computed from the parameters. This will begin a
/// new subpath if the path is empty or the previous subpath was closed.
fn arc_to(
&mut self,
rx: f32,
ry: f32,
angle: Angle,
size: ArcSize,
sweep: ArcSweep,
to: impl Into<Point>,
) -> &mut Self {
let from = self.current_point();
arc(
self,
from,
rx,
ry,
angle.to_radians(),
size,
sweep,
to.into(),
);
self
}
/// Adds an arc with the specified x- and y-radius, rotation angle, arc size,
/// and arc sweep from the current point to the specified end point. The end
/// point is considered relative to the current point. The center point of the
/// arc will be computed from the parameters. This will begin a new subpath if
/// the path is empty or the previous subpath was closed.
fn rel_arc_to(
&mut self,
rx: f32,
ry: f32,
angle: Angle,
size: ArcSize,
sweep: ArcSweep,
to: impl Into<Point>,
) -> &mut Self {
self.arc_to(rx, ry, angle, size, sweep, to.into() + self.current_point())
}
/// Closes the current subpath.
fn close(&mut self) -> &mut Self;
/// Adds a rectangle with the specified position and size to the path. This
/// will create a new closed subpath.
fn add_rect(&mut self, xy: impl Into<Point>, w: f32, h: f32) -> &mut Self {
let p = xy.into();
let (l, t, r, b) = (p.x, p.y, p.x + w, p.y + h);
self.move_to(p);
self.line_to((r, t));
self.line_to((r, b));
self.line_to((l, b));
self.close()
}
/// Adds a rounded rectangle with the specified position, size and radii to
/// the path. This will create a new closed subpath.
fn add_round_rect(
&mut self,
xy: impl Into<Point>,
w: f32,
h: f32,
rx: f32,
ry: f32,
) -> &mut Self {
let p = xy.into();
let size = ArcSize::Small;
let sweep = ArcSweep::Positive;
let a = Angle::from_radians(0.);
let hw = w * 0.5;
let rx = rx.max(0.).min(hw);
let hh = h * 0.5;
let ry = ry.max(0.).min(hh);
self.move_to((p.x + rx, p.y));
self.line_to((p.x + w - rx, p.y));
self.arc_to(rx, ry, a, size, sweep, (p.x + w, p.y + ry));
self.line_to((p.x + w, p.y + h - ry));
self.arc_to(rx, ry, a, size, sweep, (p.x + w - rx, p.y + h));
self.line_to((p.x + rx, p.y + h));
self.arc_to(rx, ry, a, size, sweep, (p.x, p.y + h - ry));
self.line_to((p.x, p.y + ry));
self.arc_to(rx, ry, a, size, sweep, (p.x + rx, p.y));
self.close()
}
/// Adds an ellipse with the specified center and radii to the path. This
/// will create a new closed subpath.
fn add_ellipse(&mut self, center: impl Into<Point>, rx: f32, ry: f32) -> &mut Self {
let center = center.into();
let cx = center.x;
let cy = center.y;
let a = 0.551915024494;
let arx = a * rx;
let ary = a * ry;
self.move_to((cx + rx, cy));
self.curve_to((cx + rx, cy + ary), (cx + arx, cy + ry), (cx, cy + ry));
self.curve_to((cx - arx, cy + ry), (cx - rx, cy + ary), (cx - rx, cy));
self.curve_to((cx - rx, cy - ary), (cx - arx, cy - ry), (cx, cy - ry));
self.curve_to((cx + arx, cy - ry), (cx + rx, cy - ary), (cx + rx, cy));
self.close()
}
/// Adds a circle with the specified center and radius to the path. This
/// will create a new closed subpath.
fn add_circle(&mut self, center: impl Into<Point>, r: f32) -> &mut Self {
self.add_ellipse(center, r, r)
}
}
impl PathBuilder for Vec<Command> {
fn current_point(&self) -> Point {
match self.last() {
None => Point::ZERO,
Some(cmd) => match cmd {
Command::MoveTo(p)
| Command::LineTo(p)
| Command::QuadTo(_, p)
| Command::CurveTo(_, _, p) => *p,
Command::Close => {
for cmd in self.iter().rev().skip(1) {
if let Command::MoveTo(p) = cmd {
return *p;
}
}
Point::ZERO
}
},
}
}
fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.push(Command::MoveTo(to.into()));
self
}
fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.push(Command::LineTo(to.into()));
self
}
fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
self.push(Command::QuadTo(control.into(), to.into()));
self
}
fn curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self {
self.push(Command::CurveTo(
control1.into(),
control2.into(),
to.into(),
));
self
}
fn close(&mut self) -> &mut Self {
self.push(Command::Close);
self
}
}
pub struct TransformSink<'a, S> {
pub sink: &'a mut S,
pub transform: Transform,
}
impl<S: PathBuilder> PathBuilder for TransformSink<'_, S> {
fn current_point(&self) -> Point {
self.sink.current_point()
}
fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.sink.move_to(self.transform.transform_point(to.into()));
self
}
fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
self.sink.line_to(self.transform.transform_point(to.into()));
self
}
fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
self.sink.quad_to(
self.transform.transform_point(control.into()),
self.transform.transform_point(to.into()),
);
self
}
fn curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self {
self.sink.curve_to(
self.transform.transform_point(control1.into()),
self.transform.transform_point(control2.into()),
self.transform.transform_point(to.into()),
);
self
}
fn close(&mut self) -> &mut Self {
self.sink.close();
self
}
}
impl PathBuilder for BoundsBuilder {
fn current_point(&self) -> Point {
self.current
}
fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
let p = to.into();
self.add(p);
self.current = p;
self
}
fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
let p = to.into();
self.add(p);
self.current = p;
self
}
fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
self.add(control.into());
let p = to.into();
self.add(p);
self.current = p;
self
}
fn curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self {
self.add(control1.into());
self.add(control2.into());
let p = to.into();
self.add(p);
self.current = p;
self
}
fn close(&mut self) -> &mut Self {
self
}
}
/// An iterator that generates cubic bezier curves for an arc.
#[derive(Copy, Clone, Default)]
pub struct Arc {
count: usize,
center: (f32, f32),
radii: (f32, f32),
cosphi: f32,
sinphi: f32,
ang1: f32,
ang2: f32,
a: f32,
}
impl Arc {
pub fn new(
from: impl Into<[f32; 2]>,
rx: f32,
ry: f32,
angle: f32,
size: ArcSize,
sweep: ArcSweep,
to: impl Into<[f32; 2]>,
) -> Self {
let from = from.into();
let to = to.into();
let (px, py) = (from[0], from[1]);
const TAU: f32 = 3.141579 * 2.;
let (sinphi, cosphi) = angle.sin_cos();
let pxp = cosphi * (px - to[0]) / 2. + sinphi * (py - to[1]) / 2.;
let pyp = -sinphi * (px - to[0]) / 2. + cosphi * (py - to[1]) / 2.;
if pxp == 0. && pyp == 0. {
return Self::default();
}
let mut rx = rx.abs();
let mut ry = ry.abs();
let lambda = pxp.powi(2) / rx.powi(2) + pyp.powi(2) / ry.powi(2);
if lambda > 1. {
let s = lambda.sqrt();
rx *= s;
ry *= s;
}
let large_arc = size == ArcSize::Large;
let sweep = sweep == ArcSweep::Positive;
let (cx, cy, ang1, mut ang2) = {
fn vec_angle(ux: f32, uy: f32, vx: f32, vy: f32) -> f32 {
let sign = if (ux * vy - uy * vx) < 0. { -1. } else { 1. };
let dot = (ux * vx + uy * vy).clamp(-1., 1.);
sign * dot.acos()
}
let rxsq = rx * rx;
let rysq = ry * ry;
let pxpsq = pxp * pxp;
let pypsq = pyp * pyp;
let mut radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq);
if radicant < 0. {
radicant = 0.;
}
radicant /= (rxsq * pypsq) + (rysq * pxpsq);
radicant = radicant.sqrt() * if large_arc == sweep { -1. } else { 1. };
let cxp = radicant * rx / ry * pyp;
let cyp = radicant * -ry / rx * pxp;
let cx = cosphi * cxp - sinphi * cyp + (px + to[0]) / 2.;
let cy = sinphi * cxp + cosphi * cyp + (py + to[1]) / 2.;
let vx1 = (pxp - cxp) / rx;
let vy1 = (pyp - cyp) / ry;
let vx2 = (-pxp - cxp) / rx;
let vy2 = (-pyp - cyp) / ry;
let ang1 = vec_angle(1., 0., vx1, vy1);
let mut ang2 = vec_angle(vx1, vy1, vx2, vy2);
if !sweep && ang2 > 0. {
ang2 -= TAU;
}
if sweep && ang2 < 0. {
ang2 += TAU;
}
(cx, cy, ang1, ang2)
};
let mut ratio = ang2.abs() / (TAU / 4.);
if (1. - ratio).abs() < 0.0000001 {
ratio = 1.
}
let segments = ratio.ceil().max(1.);
ang2 /= segments;
let a = if ang2 == f32::consts::FRAC_PI_2 {
0.551915024494
} else if ang2 == -f32::consts::FRAC_PI_2 {
-0.551915024494
} else {
4. / 3. * (ang2 / 4.).tan()
};
Self {
count: segments as usize,
center: (cx, cy),
radii: (rx, ry),
sinphi,
cosphi,
ang1,
ang2,
a,
}
}
}
impl Iterator for Arc {
type Item = Command;
fn next(&mut self) -> Option<Self::Item> {
if self.count == 0 {
return None;
}
self.count -= 1;
let (y1, x1) = self.ang1.sin_cos();
let (y2, x2) = (self.ang1 + self.ang2).sin_cos();
let a = self.a;
let (cx, cy) = self.center;
let (rx, ry) = self.radii;
let sinphi = self.sinphi;
let cosphi = self.cosphi;
let c1 = Point::new((x1 - y1 * a) * rx, (y1 + x1 * a) * ry);
let c1 = Point::new(
cx + (cosphi * c1.x - sinphi * c1.y),
cy + (sinphi * c1.x + cosphi * c1.y),
);
let c2 = Point::new((x2 + y2 * a) * rx, (y2 - x2 * a) * ry);
let c2 = Point::new(
cx + (cosphi * c2.x - sinphi * c2.y),
cy + (sinphi * c2.x + cosphi * c2.y),
);
let p = Point::new(x2 * rx, y2 * ry);
let p = Point::new(
cx + (cosphi * p.x - sinphi * p.y),
cy + (sinphi * p.x + cosphi * p.y),
);
self.ang1 += self.ang2;
Some(Command::CurveTo(c1, c2, p))
}
}
#[allow(clippy::too_many_arguments)]
pub fn arc(
sink: &mut impl PathBuilder,
from: Point,
rx: f32,
ry: f32,
angle: f32,
size: ArcSize,
sweep: ArcSweep,
to: Point,
) {
let p = from;
let (px, py) = (p.x, p.y);
const TAU: f32 = core::f32::consts::PI * 2.;
let (sinphi, cosphi) = angle.sin_cos();
let pxp = cosphi * (px - to.x) / 2. + sinphi * (py - to.y) / 2.;
let pyp = -sinphi * (px - to.x) / 2. + cosphi * (py - to.y) / 2.;
if pxp == 0. && pyp == 0. {
return;
}
let mut rx = rx.abs();
let mut ry = ry.abs();
let lambda = pxp.powi(2) / rx.powi(2) + pyp.powi(2) / ry.powi(2);
if lambda > 1. {
let s = lambda.sqrt();
rx *= s;
ry *= s;
}
let large_arc = size == ArcSize::Large;
let sweep = sweep == ArcSweep::Positive;
let (cx, cy, mut ang1, mut ang2) = {
fn vec_angle(ux: f32, uy: f32, vx: f32, vy: f32) -> f32 {
let sign = if (ux * vy - uy * vx) < 0. { -1. } else { 1. };
let dot = (ux * vx + uy * vy).clamp(-1., 1.);
sign * dot.acos()
}
let rxsq = rx * rx;
let rysq = ry * ry;
let pxpsq = pxp * pxp;
let pypsq = pyp * pyp;
let mut radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq);
if radicant < 0. {
radicant = 0.;
}
radicant /= (rxsq * pypsq) + (rysq * pxpsq);
radicant = radicant.sqrt() * if large_arc == sweep { -1. } else { 1. };
let cxp = radicant * rx / ry * pyp;
let cyp = radicant * -ry / rx * pxp;
let cx = cosphi * cxp - sinphi * cyp + (px + to.x) / 2.;
let cy = sinphi * cxp + cosphi * cyp + (py + to.y) / 2.;
let vx1 = (pxp - cxp) / rx;
let vy1 = (pyp - cyp) / ry;
let vx2 = (-pxp - cxp) / rx;
let vy2 = (-pyp - cyp) / ry;
let ang1 = vec_angle(1., 0., vx1, vy1);
let mut ang2 = vec_angle(vx1, vy1, vx2, vy2);
if !sweep && ang2 > 0. {
ang2 -= TAU;
}
if sweep && ang2 < 0. {
ang2 += TAU;
}
(cx, cy, ang1, ang2)
};
let mut ratio = ang2.abs() / (TAU / 4.);
if (1. - ratio).abs() < 0.0000001 {
ratio = 1.
}
let segments = ratio.ceil().max(1.);
ang2 /= segments;
let a = if ang2 == f32::consts::FRAC_PI_2 {
0.551915024494
} else if ang2 == -f32::consts::FRAC_PI_2 {
-0.551915024494
} else {
4. / 3. * (ang2 / 4.).tan()
};
for _ in 0..segments as usize {
let (y1, x1) = ang1.sin_cos();
let (y2, x2) = (ang1 + ang2).sin_cos();
let c1 = Point::new((x1 - y1 * a) * rx, (y1 + x1 * a) * ry);
let c1 = Point::new(
cx + (cosphi * c1.x - sinphi * c1.y),
cy + (sinphi * c1.x + cosphi * c1.y),
);
let c2 = Point::new((x2 + y2 * a) * rx, (y2 - x2 * a) * ry);
let c2 = Point::new(
cx + (cosphi * c2.x - sinphi * c2.y),
cy + (sinphi * c2.x + cosphi * c2.y),
);
let p = Point::new(x2 * rx, y2 * ry);
let p = Point::new(
cx + (cosphi * p.x - sinphi * p.y),
cy + (sinphi * p.x + cosphi * p.y),
);
sink.curve_to(c1, c2, p);
ang1 += ang2;
}
}

228
vendor/zeno/src/path_data.rs vendored Normal file
View File

@@ -0,0 +1,228 @@
//! Path data.
use super::command::{Command, PointsCommands, Verb};
use super::geometry::{Point, Transform};
use super::path_builder::PathBuilder;
use super::segment::segments;
use super::svg_parser::SvgCommands;
#[cfg(feature = "eval")]
use super::stroke::stroke_into;
#[cfg(feature = "eval")]
use super::style::*;
#[cfg(feature = "eval")]
use super::geometry::{Bounds, BoundsBuilder};
#[cfg(feature = "eval")]
use super::path_builder::TransformSink;
use crate::lib::Vec;
/// Trait for types that represent path data.
///
/// A primary design goal for this crate is to be agnostic with regard to
/// storage of path data. This trait provides the abstraction to make that
/// possible.
///
/// All path data is consumed internally as an iterator over path
/// [commands](Command) and as such, this trait is similar to
/// the `IntoIterator` trait, but restricted to iterators of commands and
/// without consuming itself.
///
/// Implementations of this trait are provided for SVG path data (in the form
/// of strings), slices/vectors of commands, and the common point and
/// verb list structure (as the tuple `(&[Point], &[Verb])`).
///
/// As such, these paths are all equivalent:
///
/// ```rust
/// use zeno::{Command, PathData, Point, Verb};
///
/// // SVG path data
/// let path1 = "M1,2 L3,4";
///
/// // Slice of commands
/// let path2 = &[
/// Command::MoveTo(Point::new(1.0, 2.0)),
/// Command::LineTo(Point::new(3.0, 4.0)),
/// ][..];
///
/// // Tuple of slices to points and verbs
/// let path3 = (
/// &[Point::new(1.0, 2.0), Point::new(3.0, 4.0)][..],
/// &[Verb::MoveTo, Verb::LineTo][..],
/// );
///
/// assert!(path1.commands().eq(path2.commands()));
/// assert!(path2.commands().eq(path3.commands()));
/// ```
///
/// Implementing `PathData` is similar to implementing `IntoIterator`:
///
/// ```rust
/// use zeno::{Command, PathData};
///
/// pub struct MyPath {
/// data: Vec<Command>
/// }
///
/// impl<'a> PathData for &'a MyPath {
/// // Copied here because PathData expects Commands by value
/// type Commands = std::iter::Copied<std::slice::Iter<'a, Command>>;
///
/// fn commands(&self) -> Self::Commands {
/// self.data.iter().copied()
/// }
/// }
/// ```
///
/// The provided `copy_into()` method evaluates the command iterator and
/// submits the commands to a sink. You should also implement this if you
/// have a more direct method of dispatching to a sink as rasterizer
/// performance can be sensitive to latencies here.
pub trait PathData {
/// Command iterator.
type Commands: Iterator<Item = Command> + Clone;
/// Returns an iterator over the commands described by the path data.
fn commands(&self) -> Self::Commands;
/// Copies the path data into the specified sink.
fn copy_to(&self, sink: &mut impl PathBuilder) {
for cmd in self.commands() {
use Command::*;
match cmd {
MoveTo(p) => sink.move_to(p),
LineTo(p) => sink.line_to(p),
QuadTo(c, p) => sink.quad_to(c, p),
CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p),
Close => sink.close(),
};
}
}
}
/// Computes the total length of the path.
pub fn length(data: impl PathData, transform: Option<Transform>) -> f32 {
let data = data.commands();
let mut length = 0.;
if let Some(transform) = transform {
for s in segments(data.map(|cmd| cmd.transform(&transform)), false) {
length += s.length();
}
} else {
for s in segments(data, false) {
length += s.length();
}
}
length
}
/// Computes the bounding box of the path.
#[cfg(feature = "eval")]
pub fn bounds<'a>(
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
) -> Bounds {
let style = style.into();
let mut bounds = BoundsBuilder::new();
apply(data, style, transform, &mut bounds);
bounds.build()
}
/// Applies the style and transform to the path and emits the result to the
/// specified sink.
#[cfg(feature = "eval")]
pub fn apply<'a>(
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
sink: &mut impl PathBuilder,
) -> Fill {
let style = style.into();
match style {
Style::Fill(fill) => {
if let Some(transform) = transform {
let mut transform_sink = TransformSink { sink, transform };
data.copy_to(&mut transform_sink);
fill
} else {
data.copy_to(sink);
fill
}
}
Style::Stroke(stroke) => {
if let Some(transform) = transform {
if stroke.scale {
let mut transform_sink = TransformSink { sink, transform };
stroke_into(data.commands(), &stroke, &mut transform_sink);
} else {
stroke_into(
data.commands().map(|cmd| cmd.transform(&transform)),
&stroke,
sink,
);
}
} else {
stroke_into(data.commands(), &stroke, sink);
}
Fill::NonZero
}
}
}
impl<T> PathData for &'_ T
where
T: PathData,
{
type Commands = T::Commands;
fn commands(&self) -> Self::Commands {
T::commands(*self)
}
#[inline(always)]
fn copy_to(&self, sink: &mut impl PathBuilder) {
T::copy_to(*self, sink)
}
}
impl<'a> PathData for &'a str {
type Commands = SvgCommands<'a>;
fn commands(&self) -> Self::Commands {
SvgCommands::new(self)
}
}
impl<'a> PathData for (&'a [Point], &'a [Verb]) {
type Commands = PointsCommands<'a>;
fn commands(&self) -> Self::Commands {
PointsCommands::new(self.0, self.1)
}
#[inline(always)]
fn copy_to(&self, sink: &mut impl PathBuilder) {
self.commands().copy_to(sink);
}
}
impl<'a> PathData for &'a [Command] {
type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
fn commands(&self) -> Self::Commands {
self.iter().copied()
}
}
impl<'a> PathData for &'a Vec<Command> {
type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
fn commands(&self) -> Self::Commands {
self.iter().copied()
}
}

831
vendor/zeno/src/raster.rs vendored Normal file
View File

@@ -0,0 +1,831 @@
//! Path rasterizer.
#![allow(clippy::too_many_arguments)]
use super::geometry::{Point, Vector};
use super::path_builder::PathBuilder;
use super::style::Fill;
use crate::lib::Vec;
use core::fmt;
#[inline(always)]
fn coverage(fill: Fill, mut coverage: i32) -> u8 {
coverage >>= PIXEL_BITS * 2 + 1 - 8;
if fill == Fill::EvenOdd {
coverage &= 511;
if coverage >= 256 {
coverage = 511i32.wrapping_sub(coverage);
}
} else {
if coverage < 0 {
coverage = !coverage;
}
if coverage >= 256 {
coverage = 255;
}
}
coverage as u8
}
pub struct Rasterizer<'a, S: RasterStorage> {
storage: &'a mut S,
xmin: i32,
xmax: i32,
ymin: i32,
ymax: i32,
height: i32,
shift: Vector,
start: FixedPoint,
closed: bool,
current: Point,
x: i32,
y: i32,
px: i32,
py: i32,
cover: i32,
area: i32,
invalid: bool,
}
impl<'a, S: RasterStorage> Rasterizer<'a, S> {
pub fn new(storage: &'a mut S) -> Self {
Self {
storage,
xmin: 0,
xmax: 0,
ymin: 0,
ymax: 0,
height: 0,
shift: Vector::ZERO,
start: FixedPoint::default(),
closed: false,
current: Point::ZERO,
x: 0,
y: 0,
px: 0,
py: 0,
cover: 0,
area: 0,
invalid: false,
}
}
pub fn rasterize(
&mut self,
shift: Vector,
width: u32,
height: u32,
apply: &mut impl FnMut(&mut Self),
fill: Fill,
buffer: &mut [u8],
pitch: usize,
y_up: bool,
) {
let w = width as i32;
let h = height as i32;
self.storage
.reset(FixedPoint { x: 0, y: 0 }, FixedPoint { x: w, y: h });
self.shift = shift;
self.start = FixedPoint::default();
self.closed = true;
self.current = Point::ZERO;
self.xmin = 0;
self.ymin = 0;
self.xmax = w;
self.ymax = h;
self.height = h;
self.x = 0;
self.y = 0;
self.px = 0;
self.py = 0;
self.invalid = true;
apply(self);
if !self.closed {
self.line_to(self.start);
}
if !self.invalid {
self.storage.set(self.x, self.y, self.area, self.cover);
}
let indices = self.storage.indices();
let cells = self.storage.cells();
let min = FixedPoint::new(self.xmin, self.ymin);
let max = FixedPoint::new(self.xmax, self.ymax);
let height = height as usize;
for (i, &index) in indices.iter().enumerate() {
if index != -1 {
let y = ((i as i32) - min.y) as usize;
let row_offset = if y_up {
pitch * (height - 1 - y)
} else {
pitch * y
};
let row = &mut buffer[row_offset..];
let mut x = min.x;
let mut cover = 0;
let mut area;
let mut index = index;
loop {
let cell = &cells[index as usize];
if cover != 0 && cell.x > x {
let count = (cell.x - x) as usize;
let c = coverage(fill, cover);
let xi = x as usize;
for b in &mut row[xi..xi + count] {
*b = c;
}
}
cover = cover.wrapping_add(cell.cover.wrapping_mul(ONE_PIXEL * 2));
area = cover.wrapping_sub(cell.area);
if area != 0 && cell.x >= min.x {
let count = 1;
let c = coverage(fill, area);
let xi = cell.x as usize;
for b in &mut row[xi..xi + count] {
*b = c;
}
}
x = cell.x + 1;
index = cell.next;
if index == -1 {
break;
}
}
if cover != 0 {
let count = (max.x - x) as usize;
let c = coverage(fill, cover);
let xi = x as usize;
for b in &mut row[xi..xi + count] {
*b = c;
}
}
}
}
}
pub fn rasterize_write(
&mut self,
shift: Vector,
width: u32,
height: u32,
apply: &mut impl FnMut(&mut Self),
fill: Fill,
pitch: usize,
y_up: bool,
write: &mut impl FnMut(usize, usize, usize, u8),
) {
let w = width as i32;
let h = height as i32;
self.storage
.reset(FixedPoint { x: 0, y: 0 }, FixedPoint { x: w, y: h });
self.shift = shift;
self.start = FixedPoint::default();
self.closed = true;
self.current = Point::ZERO;
self.xmin = 0;
self.ymin = 0;
self.xmax = w;
self.ymax = h;
self.height = h;
self.x = 0;
self.y = 0;
self.px = 0;
self.py = 0;
self.invalid = true;
apply(self);
if !self.closed {
self.line_to(self.start);
}
if !self.invalid {
self.storage.set(self.x, self.y, self.area, self.cover);
}
let indices = self.storage.indices();
let cells = self.storage.cells();
let min = FixedPoint::new(self.xmin, self.ymin);
let max = FixedPoint::new(self.xmax, self.ymax);
let height = height as usize;
for (i, &index) in indices.iter().enumerate() {
if index != -1 {
let y = ((i as i32) - min.y) as usize;
let row_offset = if y_up {
pitch * (height - 1 - y)
} else {
pitch * y
};
let mut x = min.x;
let mut cover = 0;
let mut area;
let mut index = index;
loop {
let cell = &cells[index as usize];
if cover != 0 && cell.x > x {
let count = (cell.x - x) as usize;
let c = coverage(fill, cover);
let xi = x as usize;
write(row_offset, xi, count, c);
}
cover = cover.wrapping_add(cell.cover.wrapping_mul(ONE_PIXEL * 2));
area = cover.wrapping_sub(cell.area);
if area != 0 && cell.x >= min.x {
let count = 1;
let c = coverage(fill, area);
let xi = cell.x as usize;
write(row_offset, xi, count, c);
}
x = cell.x + 1;
index = cell.next;
if index == -1 {
break;
}
}
if cover != 0 {
let count = (max.x - x) as usize;
let c = coverage(fill, cover);
let xi = x as usize;
write(row_offset, xi, count, c);
}
}
}
}
#[inline(always)]
fn set_cell(&mut self, x: i32, y: i32) {
if !self.invalid && (self.area != 0 || self.cover != 0) {
self.storage.set(self.x, self.y, self.area, self.cover);
}
self.area = 0;
self.cover = 0;
self.x = if x > (self.xmin - 1) {
x
} else {
self.xmin - 1
};
self.y = y;
self.invalid = y >= self.ymax || y < self.ymin || x >= self.xmax;
}
fn move_to(&mut self, to: FixedPoint) {
self.set_cell(trunc(to.x), trunc(to.y));
self.px = to.x;
self.py = to.y;
}
fn line_to(&mut self, to: FixedPoint) {
let to_x = to.x;
let to_y = to.y;
let mut ey1 = trunc(self.py);
let ey2 = trunc(to_y);
if (ey1 >= self.ymax && ey2 >= self.ymax) || (ey1 < self.ymin && ey2 < self.ymin) {
self.px = to_x;
self.py = to_y;
return;
}
let mut ex1 = trunc(self.px);
let ex2 = trunc(to_x);
let mut fx1 = fract(self.px);
let mut fy1 = fract(self.py);
let dx = to_x - self.px;
let dy = to_y - self.py;
if ex1 == ex2 && ey1 == ey2 {
// empty
} else if dy == 0 {
self.set_cell(ex2, ey2);
self.px = to_x;
self.py = to_y;
return;
} else if dx == 0 {
if dy > 0 {
loop {
let fy2 = ONE_PIXEL;
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * fx1 * 2;
fy1 = 0;
ey1 += 1;
self.set_cell(ex1, ey1);
if ey1 == ey2 {
break;
}
}
} else {
loop {
let fy2 = 0;
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * fx1 * 2;
fy1 = ONE_PIXEL;
ey1 -= 1;
self.set_cell(ex1, ey1);
if ey1 == ey2 {
break;
}
}
}
} else {
let mut prod = dx * fy1 - dy * fx1;
let dx_r = if ex1 != ex2 { (0x00FFFFFF) / dx } else { 0 };
let dy_r = if ey1 != ey2 { (0x00FFFFFF) / dy } else { 0 };
fn udiv(a: i32, b: i32) -> i32 {
((a as u64 * b as u64) >> (4 * 8 - PIXEL_BITS)) as i32
}
loop {
if prod <= 0 && prod - dx * ONE_PIXEL > 0 {
let fx2 = 0;
let fy2 = udiv(-prod, -dx_r);
prod -= dy * ONE_PIXEL;
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * (fx1 + fx2);
fx1 = ONE_PIXEL;
fy1 = fy2;
ex1 -= 1;
} else if prod - dx * ONE_PIXEL <= 0 && prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 {
prod -= dx * ONE_PIXEL;
let fx2 = udiv(-prod, dy_r);
let fy2 = ONE_PIXEL;
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * (fx1 + fx2);
fx1 = fx2;
fy1 = 0;
ey1 += 1;
} else if prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && prod + dy * ONE_PIXEL >= 0
{
prod += dy * ONE_PIXEL;
let fx2 = ONE_PIXEL;
let fy2 = udiv(prod, dx_r);
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * (fx1 + fx2);
fx1 = 0;
fy1 = fy2;
ex1 += 1;
} else {
let fx2 = udiv(prod, -dy_r);
let fy2 = 0;
prod += dx * ONE_PIXEL;
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * (fx1 + fx2);
fx1 = fx2;
fy1 = ONE_PIXEL;
ey1 -= 1;
}
self.set_cell(ex1, ey1);
if ex1 == ex2 && ey1 == ey2 {
break;
}
}
}
let fx2 = fract(to_x);
let fy2 = fract(to_y);
self.cover += fy2 - fy1;
self.area += (fy2 - fy1) * (fx1 + fx2);
self.px = to_x;
self.py = to_y;
}
#[allow(clippy::uninit_assumed_init, invalid_value)]
fn quad_to(&mut self, control: FixedPoint, to: FixedPoint) {
let mut arc = [FixedPoint::default(); 16 * 2 + 1];
arc[0].x = to.x;
arc[0].y = to.y;
arc[1].x = control.x;
arc[1].y = control.y;
arc[2].x = self.px;
arc[2].y = self.py;
if (trunc(arc[0].y) >= self.ymax
&& trunc(arc[1].y) >= self.ymax
&& trunc(arc[2].y) >= self.ymax)
|| (trunc(arc[0].y) < self.ymin
&& trunc(arc[1].y) < self.ymin
&& trunc(arc[2].y) < self.ymin)
{
self.px = arc[0].x;
self.py = arc[0].y;
return;
}
let mut dx = (arc[2].x + arc[0].x - 2 * arc[1].x).abs();
let dy = (arc[2].y + arc[0].y - 2 * arc[1].y).abs();
if dx < dy {
dx = dy;
}
let mut draw = 1;
while dx > ONE_PIXEL / 4 {
dx >>= 2;
draw <<= 1;
}
let mut a = 0;
loop {
let mut split = draw & (-draw);
loop {
split >>= 1;
if split == 0 {
break;
}
split_quad(&mut arc[a..]);
a += 2;
}
let p = arc[a];
self.line_to(p);
draw -= 1;
if draw == 0 {
break;
}
a -= 2;
}
}
#[allow(clippy::uninit_assumed_init, invalid_value)]
fn curve_to(&mut self, control1: FixedPoint, control2: FixedPoint, to: FixedPoint) {
let mut arc = [FixedPoint::default(); 16 * 8 + 1];
arc[0].x = to.x;
arc[0].y = to.y;
arc[1].x = control2.x;
arc[1].y = control2.y;
arc[2].x = control1.x;
arc[2].y = control1.y;
arc[3].x = self.px;
arc[3].y = self.py;
if (trunc(arc[0].y) >= self.ymax
&& trunc(arc[1].y) >= self.ymax
&& trunc(arc[2].y) >= self.ymax
&& trunc(arc[3].y) >= self.ymax)
|| (trunc(arc[0].y) < self.ymin
&& trunc(arc[1].y) < self.ymin
&& trunc(arc[2].y) < self.ymin
&& trunc(arc[3].y) < self.ymin)
{
self.px = arc[0].x;
self.py = arc[0].y;
return;
}
let mut a = 0;
loop {
if (2 * arc[a].x - 3 * arc[a + 1].x + arc[a + 3].x).abs() > ONE_PIXEL / 2
|| (2 * arc[a].y - 3 * arc[a + 1].y + arc[a + 3].y).abs() > ONE_PIXEL / 2
|| (arc[a].x - 3 * arc[a + 2].x + 2 * arc[a + 3].x).abs() > ONE_PIXEL / 2
|| (arc[a].y - 3 * arc[a + 2].y + 2 * arc[a + 3].y).abs() > ONE_PIXEL / 2
{
let buf = &mut arc[a..];
// if buf.len() < 7 {
// return;
// }
if buf.len() >= 7 {
split_cubic(buf);
a += 3;
continue;
} else {
self.line_to(to);
return;
}
}
let p = arc[a];
self.line_to(p);
if a == 0 {
return;
}
a -= 3;
}
}
}
impl<S: RasterStorage> PathBuilder for Rasterizer<'_, S> {
fn current_point(&self) -> Point {
self.current + self.shift
}
#[inline(always)]
fn move_to(&mut self, to: impl Into<Point>) -> &mut Self {
if !self.closed {
self.line_to(self.start);
}
let to = to.into();
let p = FixedPoint::from_point(to + self.shift);
self.move_to(p);
self.closed = false;
self.start = p;
self.current = to;
self
}
#[inline(always)]
fn line_to(&mut self, to: impl Into<Point>) -> &mut Self {
let to = to.into();
self.current = to;
self.closed = false;
self.line_to(FixedPoint::from_point(to + self.shift));
self
}
#[inline(always)]
fn quad_to(&mut self, control: impl Into<Point>, to: impl Into<Point>) -> &mut Self {
let to = to.into();
self.current = to;
self.closed = false;
self.quad_to(
FixedPoint::from_point(control.into() + self.shift),
FixedPoint::from_point(to + self.shift),
);
self
}
#[inline(always)]
fn curve_to(
&mut self,
control1: impl Into<Point>,
control2: impl Into<Point>,
to: impl Into<Point>,
) -> &mut Self {
let to = to.into();
self.current = to;
self.closed = false;
self.curve_to(
FixedPoint::from_point(control1.into() + self.shift),
FixedPoint::from_point(control2.into() + self.shift),
FixedPoint::from_point(to + self.shift),
);
self
}
#[inline(always)]
fn close(&mut self) -> &mut Self {
self.line_to(self.start);
self.closed = true;
self
}
}
#[derive(Copy, Clone, Default)]
pub struct Cell {
x: i32,
cover: i32,
area: i32,
next: i32,
}
pub trait RasterStorage {
fn reset(&mut self, min: FixedPoint, max: FixedPoint);
fn cells(&self) -> &[Cell];
fn indices(&self) -> &[i32];
fn set(&mut self, x: i32, y: i32, area: i32, cover: i32);
}
#[derive(Default)]
pub struct HeapStorage {
min: FixedPoint,
max: FixedPoint,
cells: Vec<Cell>,
indices: Vec<i32>,
}
impl RasterStorage for HeapStorage {
fn reset(&mut self, min: FixedPoint, max: FixedPoint) {
self.min = min;
self.max = max;
self.cells.clear();
self.indices.clear();
self.indices.resize((max.y - min.y) as usize, -1);
}
fn cells(&self) -> &[Cell] {
&self.cells
}
fn indices(&self) -> &[i32] {
&self.indices
}
#[inline(always)]
#[allow(clippy::comparison_chain)]
fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) {
let yindex = (y - self.min.y) as usize;
let mut cell_index = self.indices[yindex];
let mut last_index = -1;
while cell_index != -1 {
let cell = &mut self.cells[cell_index as usize];
if cell.x > x {
break;
} else if cell.x == x {
cell.area = cell.area.wrapping_add(area);
cell.cover = cell.cover.wrapping_add(cover);
return;
}
last_index = cell_index;
cell_index = cell.next;
}
let new_index = self.cells.len();
let cell = Cell {
x,
area,
cover,
next: cell_index,
};
if last_index != -1 {
self.cells[last_index as usize].next = new_index as i32;
} else {
self.indices[yindex] = new_index as i32;
}
self.cells.push(cell);
}
}
const MAX_CELLS: usize = 1024;
const MAX_BAND: usize = 512;
pub struct AdaptiveStorage {
min: FixedPoint,
max: FixedPoint,
height: usize,
cell_count: usize,
cells: [Cell; MAX_CELLS],
heap_cells: Vec<Cell>,
indices: [i32; MAX_BAND],
heap_indices: Vec<i32>,
}
impl AdaptiveStorage {
#[allow(clippy::uninit_assumed_init, invalid_value)]
pub fn new() -> Self {
Self {
min: FixedPoint::default(),
max: FixedPoint::default(),
height: 0,
cell_count: 0,
cells: [Default::default(); MAX_CELLS],
heap_cells: Vec::new(),
indices: [Default::default(); MAX_BAND],
heap_indices: Vec::new(),
}
}
}
impl RasterStorage for AdaptiveStorage {
fn reset(&mut self, min: FixedPoint, max: FixedPoint) {
self.min = min;
self.max = max;
self.height = (max.y - min.y) as usize;
self.cell_count = 0;
self.heap_cells.clear();
self.heap_indices.clear();
if self.height > MAX_BAND {
self.heap_indices.resize((max.y - min.y) as usize, -1);
} else {
for i in 0..self.height {
self.indices[i] = -1;
}
}
}
fn cells(&self) -> &[Cell] {
if self.cell_count > MAX_CELLS {
&self.heap_cells
} else {
&self.cells
}
}
fn indices(&self) -> &[i32] {
if self.height > MAX_BAND {
&self.heap_indices
} else {
&self.indices[..self.height]
}
}
#[inline(always)]
#[allow(clippy::comparison_chain)]
fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) {
let yindex = (y - self.min.y) as usize;
let indices = if self.height > MAX_BAND {
&mut self.heap_indices[..]
} else {
&mut self.indices[..]
};
let cells = if !self.heap_cells.is_empty() {
&mut self.heap_cells[..]
} else {
&mut self.cells[..]
};
let mut cell_index = indices[yindex];
let mut last_index = -1;
while cell_index != -1 {
let cell = &mut cells[cell_index as usize];
if cell.x > x {
break;
} else if cell.x == x {
cell.area = cell.area.wrapping_add(area);
cell.cover = cell.cover.wrapping_add(cover);
return;
}
last_index = cell_index;
cell_index = cell.next;
}
let new_index = self.cell_count;
self.cell_count += 1;
let cell = Cell {
x,
area,
cover,
next: cell_index,
};
if last_index != -1 {
cells[last_index as usize].next = new_index as i32;
} else {
indices[yindex] = new_index as i32;
}
if new_index < MAX_CELLS {
cells[new_index] = cell;
} else {
if self.heap_cells.is_empty() {
self.heap_cells.extend_from_slice(&self.cells);
}
self.heap_cells.push(cell);
}
}
}
const _MAX_DIM: u32 = i16::MAX as u32;
fn split_quad(base: &mut [FixedPoint]) {
let mut a;
let mut b;
base[4].x = base[2].x;
a = base[0].x + base[1].x;
b = base[1].x + base[2].x;
base[3].x = b >> 1;
base[2].x = (a + b) >> 2;
base[1].x = a >> 1;
base[4].y = base[2].y;
a = base[0].y + base[1].y;
b = base[1].y + base[2].y;
base[3].y = b >> 1;
base[2].y = (a + b) >> 2;
base[1].y = a >> 1;
}
fn split_cubic(base: &mut [FixedPoint]) {
let mut a;
let mut b;
let mut c;
base[6].x = base[3].x;
a = base[0].x + base[1].x;
b = base[1].x + base[2].x;
c = base[2].x + base[3].x;
base[5].x = c >> 1;
c += b;
base[4].x = c >> 2;
base[1].x = a >> 1;
a += b;
base[2].x = a >> 2;
base[3].x = (a + c) >> 3;
base[6].y = base[3].y;
a = base[0].y + base[1].y;
b = base[1].y + base[2].y;
c = base[2].y + base[3].y;
base[5].y = c >> 1;
c += b;
base[4].y = c >> 2;
base[1].y = a >> 1;
a += b;
base[2].y = a >> 2;
base[3].y = (a + c) >> 3;
}
#[derive(Copy, Clone, Default)]
pub struct FixedPoint {
pub x: i32,
pub y: i32,
}
impl fmt::Debug for FixedPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl FixedPoint {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
#[inline(always)]
pub fn from_point(p: Point) -> Self {
Self {
x: to_fixed(p.x),
y: to_fixed(p.y),
}
}
}
#[inline(always)]
fn to_fixed(v: f32) -> i32 {
unsafe { (v * 256.).to_int_unchecked() }
}
const PIXEL_BITS: i32 = 8;
const ONE_PIXEL: i32 = 1 << PIXEL_BITS;
#[inline(always)]
fn trunc(x: i32) -> i32 {
x >> PIXEL_BITS
}
#[inline(always)]
fn fract(x: i32) -> i32 {
x & (ONE_PIXEL - 1)
}

102
vendor/zeno/src/scratch.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
//! Context for reusing dynamic memory allocations.
use super::geometry::{Bounds, BoundsBuilder, Transform};
use super::path_builder::{PathBuilder, TransformSink};
use super::path_data::PathData;
use super::raster::HeapStorage;
use super::segment::Segment;
use super::stroke::stroke_with_storage;
use super::style::{Fill, Style};
use crate::lib::Vec;
use core::borrow::Borrow;
/// Scratch memory for reusable heap allocations.
#[derive(Default)]
pub struct Scratch {
pub(super) inner: Inner,
pub(super) render: HeapStorage,
}
impl Scratch {
/// Creates a new scratch memory context.
pub fn new() -> Self {
Self::default()
}
/// Applies the style and transform to the path and emits the result to the specified sink.
pub fn apply<'a>(
&mut self,
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
sink: &mut impl PathBuilder,
) -> Fill {
self.inner.apply(data, &style.into(), transform, sink)
}
/// Computes the bounding box of the path.
pub fn bounds<'a>(
&mut self,
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
) -> Bounds {
let style = style.into();
let mut bounds = BoundsBuilder::new();
self.apply(data, style, transform, &mut bounds);
bounds.build()
}
}
#[derive(Default)]
pub(super) struct Inner {
pub segments: Vec<Segment>,
}
impl Inner {
pub fn apply(
&mut self,
data: impl PathData,
style: &Style,
transform: Option<Transform>,
sink: &mut impl PathBuilder,
) -> Fill {
match style {
Style::Fill(fill) => {
if let Some(transform) = transform {
let mut transform_sink = TransformSink { sink, transform };
data.copy_to(&mut transform_sink);
*fill
} else {
data.copy_to(sink);
*fill
}
}
Style::Stroke(stroke) => {
if let Some(transform) = transform {
if stroke.scale {
let mut transform_sink = TransformSink { sink, transform };
stroke_with_storage(
data.commands(),
stroke,
&mut transform_sink,
&mut self.segments,
);
} else {
stroke_with_storage(
data.commands()
.map(|cmd| cmd.borrow().transform(&transform)),
stroke,
sink,
&mut self.segments,
);
}
} else {
stroke_with_storage(data.commands(), stroke, sink, &mut self.segments);
}
Fill::NonZero
}
}
}
}

650
vendor/zeno/src/segment.rs vendored Normal file
View File

@@ -0,0 +1,650 @@
//! Path segmentation.
#![allow(clippy::excessive_precision)]
use super::command::Command;
use super::geometry::*;
#[allow(unused)]
use super::F32Ext;
use core::borrow::Borrow;
use core::f32;
/// Represents the time parameter for a specific distance along
/// a segment.
#[derive(Copy, Clone, Debug)]
pub struct SegmentTime {
pub distance: f32,
pub time: f32,
}
/// Line segment.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Line {
pub a: Point,
pub b: Point,
}
impl Line {
/// Creates a new line segment.
pub fn new(a: impl Into<Vector>, b: impl Into<Vector>) -> Self {
Self {
a: a.into(),
b: b.into(),
}
}
/// Returns the length of the line segment.
pub fn length(&self) -> f32 {
(self.b - self.a).length()
}
/// Returns a slice of the line segment described by the specified start and end times.
#[allow(unused)]
pub fn slice(&self, start: f32, end: f32) -> Self {
let dir = self.b - self.a;
Self::new(self.a + dir * start, self.a + dir * end)
}
#[allow(unused)]
pub fn time(&self, distance: f32) -> SegmentTime {
let len = (self.b - self.a).length();
if distance > len {
return SegmentTime {
distance: len,
time: 1.,
};
}
SegmentTime {
distance,
time: distance / len,
}
}
#[allow(unused)]
pub fn reverse(&self) -> Self {
Self::new(self.b, self.a)
}
}
/// Cubic bezier curve.
#[derive(Copy, Clone, PartialEq, Default, Debug)]
pub struct Curve {
pub a: Point,
pub b: Point,
pub c: Point,
pub d: Point,
}
impl Curve {
/// Creates a new curve.
pub fn new(
a: impl Into<Point>,
b: impl Into<Point>,
c: impl Into<Point>,
d: impl Into<Point>,
) -> Self {
Curve {
a: a.into(),
b: b.into(),
c: c.into(),
d: d.into(),
}
}
/// Creates a new curve from a quadratic bezier curve.
pub fn from_quadratic(a: impl Into<Point>, b: impl Into<Point>, c: impl Into<Point>) -> Self {
let a = a.into();
let b = b.into();
let c = c.into();
Self {
a,
b: Point::new(a.x + 2. / 3. * (b.x - a.x), a.y + 2. / 3. * (b.y - a.y)),
c: Point::new(c.x + 2. / 3. * (b.x - c.x), c.y + 2. / 3. * (b.y - c.y)),
d: c,
}
}
/// Returns the length of the curve.
pub fn length(&self) -> f32 {
let mut len = 0.;
let mut prev = self.a;
let steps = 64;
let step = 1. / steps as f32;
let mut t = 0.;
for _ in 0..=steps {
t += step;
let next = self.evaluate(t);
len += (next - prev).length();
prev = next;
}
len
}
/// Returns a slice of the curve described by the specified start and end times.
pub fn slice(&self, start: f32, end: f32) -> Self {
let t0 = start;
let t1 = end;
let u0 = 1. - t0;
let u1 = 1. - t1;
let v0 = self.a;
let v1 = self.b;
let v2 = self.c;
let v3 = self.d;
Self::new(
(v0 * (u0 * u0 * u0))
+ (v1 * (t0 * u0 * u0 + u0 * t0 * u0 + u0 * u0 * t0))
+ (v2 * (t0 * t0 * u0 + u0 * t0 * t0 + t0 * u0 * t0))
+ (v3 * (t0 * t0 * t0)),
(v0 * (u0 * u0 * u1))
+ (v1 * (t0 * u0 * u1 + u0 * t0 * u1 + u0 * u0 * t1))
+ (v2 * (t0 * t0 * u1 + u0 * t0 * t1 + t0 * u0 * t1))
+ (v3 * (t0 * t0 * t1)),
(v0 * (u0 * u1 * u1))
+ (v1 * (t0 * u1 * u1 + u0 * t1 * u1 + u0 * u1 * t1))
+ (v2 * (t0 * t1 * u1 + u0 * t1 * t1 + t0 * u1 * t1))
+ (v3 * (t0 * t1 * t1)),
(v0 * (u1 * u1 * u1))
+ (v1 * (t1 * u1 * u1 + u1 * t1 * u1 + u1 * u1 * t1))
+ (v2 * (t1 * t1 * u1 + u1 * t1 * t1 + t1 * u1 * t1))
+ (v3 * (t1 * t1 * t1)),
)
}
/// Returns a curve with the direction reversed.
#[allow(unused)]
pub fn reverse(&self) -> Self {
Self::new(self.d, self.c, self.b, self.a)
}
/// Returns the time parameter for the specified linear distance along
/// the curve.
#[allow(unused)]
pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime {
let (distance, time) = self.time_impl(distance, tolerance, 1., 0);
SegmentTime { distance, time }
}
/// Returns true if the curve can be represented as a line within some
/// tolerance.
pub fn is_line(&self, tolerance: f32) -> bool {
let degen_ab = self.a.nearly_eq_by(self.b, tolerance);
let degen_bc = self.b.nearly_eq_by(self.c, tolerance);
let degen_cd = self.c.nearly_eq_by(self.d, tolerance);
degen_ab as u8 + degen_bc as u8 + degen_cd as u8 >= 2
}
/// Evaluates the curve at the specified time.
pub fn evaluate(&self, time: f32) -> Point {
let t = time;
let t0 = 1. - t;
(self.a * (t0 * t0 * t0))
+ (self.b * (3. * t0 * t0 * t))
+ (self.c * (3. * t0 * t * t))
+ (self.d * (t * t * t))
}
#[allow(clippy::wrong_self_convention)]
fn to_segment(&self, id: SegmentId) -> Option<Segment> {
if self.is_line(MERGE_EPSILON) {
if self.a.nearly_eq_by(self.d, MERGE_EPSILON) {
None
} else {
Some(Segment::Line(id, Line::new(self.a, self.d)))
}
} else {
Some(Segment::Curve(id, *self))
}
}
fn split_at_max_curvature(&self, splits: &mut [Curve; 4]) -> usize {
let mut tmp = [0f32; 3];
let count1 = self.max_curvature(&mut tmp);
let mut count = 0;
let mut ts = [0f32; 4];
for &t in &tmp[..count1] {
if t > 0. && t < 1. {
ts[count] = t;
count += 1;
}
}
if count == 0 {
splits[0] = *self;
} else {
let mut i = 0;
let mut last_t = 0.;
for &t in &ts[..count] {
splits[i] = self.slice(last_t, t);
i += 1;
last_t = t;
}
splits[i] = self.slice(last_t, 1.);
}
count + 1
}
fn split(&self, t: f32) -> (Self, Self) {
(self.slice(0., t), self.slice(t, 1.))
}
fn time_impl(&self, distance: f32, tolerance: f32, t: f32, level: u8) -> (f32, f32) {
if level < 5 && self.too_curvy(tolerance) {
let c0 = self.slice(0., 0.5);
let (dist0, t0) = c0.time_impl(distance, tolerance, t * 0.5, level + 1);
if dist0 < distance {
let c1 = self.slice(0.5, 1.);
let (dist1, t1) = c1.time_impl(distance - dist0, tolerance, t * 0.5, level + 1);
(dist0 + dist1, t0 + t1)
} else {
(dist0, t0)
}
} else {
let dist = (self.d - self.a).length();
if dist >= distance {
let s = distance / dist;
(distance, t * s)
} else {
(dist, t)
}
}
}
fn max_curvature(&self, ts: &mut [f32; 3]) -> usize {
let comps_x = [self.a.x, self.b.x, self.c.x, self.d.x];
let comps_y = [self.a.y, self.b.y, self.c.y, self.d.y];
fn get_coeffs(src: [f32; 4]) -> [f32; 4] {
let a = src[1] - src[0];
let b = src[2] - 2. * src[1] + src[0];
let c = src[3] + 3. * (src[1] - src[2]) - src[0];
[c * c, 3. * b * c, 2. * b * b + c * a, a * b]
}
let mut coeffs = get_coeffs(comps_x);
let coeffs_y = get_coeffs(comps_y);
for i in 0..4 {
coeffs[i] += coeffs_y[i];
}
Self::solve(coeffs, ts)
}
fn solve(coeff: [f32; 4], ts: &mut [f32; 3]) -> usize {
const PI: f32 = core::f32::consts::PI;
let i = 1. / coeff[0];
let a = coeff[1] * i;
let b = coeff[2] * i;
let c = coeff[3] * i;
let q = (a * a - b * 3.) / 9.;
let r = (2. * a * a * a - 9. * a * b + 27. * c) / 54.;
let q3 = q * q * q;
let r2_sub_q3 = r * r - q3;
let adiv3 = a / 3.;
if r2_sub_q3 < 0. {
let theta = satf32(r / q3.sqrt()).acos();
let neg2_root_q = -2. * q.sqrt();
ts[0] = satf32(neg2_root_q * (theta / 3.).cos() - adiv3);
ts[1] = satf32(neg2_root_q * ((theta + 2. * PI) / 3.).cos() - adiv3);
ts[2] = satf32(neg2_root_q * ((theta - 2. * PI) / 3.).cos() - adiv3);
ts.sort_unstable_by(|x, y| x.partial_cmp(y).unwrap_or(core::cmp::Ordering::Less));
let mut count = 3;
if ts[0] == ts[1] {
ts[1] = ts[2];
count -= 1;
}
if ts[1] == ts[2] {
count -= 1;
}
count
} else {
let mut a = r.abs() + r2_sub_q3.sqrt();
a = a.powf(0.3333333);
if r > 0. {
a = -a;
}
if a != 0. {
a += q / a;
}
ts[0] = satf32(a - adiv3);
1
}
}
fn too_curvy(&self, tolerance: f32) -> bool {
(2. * self.d.x - 3. * self.c.x + self.a.x).abs() > tolerance
|| (2. * self.d.y - 3. * self.c.y + self.a.y).abs() > tolerance
|| (self.d.x - 3. * self.b.x + 2. * self.a.x).abs() > tolerance
|| (self.d.y - 3. * self.b.y + 2. * self.a.y).abs() > tolerance
}
fn needs_split(&self) -> bool {
if self.b.nearly_eq_by(self.c, MERGE_EPSILON) {
return true;
}
let normal_ab = normal(self.a, self.b);
let normal_bc = normal(self.b, self.c);
fn too_curvy(n0: Vector, n1: Vector) -> bool {
const FLAT_ENOUGH: f32 = f32::consts::SQRT_2 / 2. + 1. / 10.;
n0.dot(n1) <= FLAT_ENOUGH
}
too_curvy(normal_ab, normal_bc) || too_curvy(normal_bc, normal(self.c, self.d))
}
}
fn satf32(x: f32) -> f32 {
x.clamp(0., 1.)
}
/// Marker that allows regrouping of previously split segments due to simplification.
pub type SegmentId = u8;
/// Segment of a path.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Segment {
/// Line segment..
Line(SegmentId, Line),
/// Cubic bezier segment.
Curve(SegmentId, Curve),
/// Marks the end of a subpath. Contains the value `true` if the subpath
/// is closed.
End(bool),
}
impl Segment {
pub fn length(&self) -> f32 {
match self {
Self::Line(_, line) => line.length(),
Self::Curve(_, curve) => curve.length(),
_ => 0.,
}
}
#[allow(unused)]
pub fn slice(&self, start: f32, end: f32) -> Self {
match self {
Self::Line(id, line) => Self::Line(*id, line.slice(start, end)),
Self::Curve(id, curve) => Self::Curve(*id, curve.slice(start, end)),
Self::End(..) => *self,
}
}
#[allow(unused)]
pub fn reverse(&self) -> Self {
match self {
Self::Line(id, line) => Self::Line(*id, line.reverse()),
Self::Curve(id, curve) => Self::Curve(*id, curve.reverse()),
Self::End(..) => *self,
}
}
#[allow(unused)]
pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime {
match self {
Self::Line(_, line) => line.time(distance),
Self::Curve(_, curve) => curve.time(distance, tolerance),
_ => SegmentTime {
distance: 0.,
time: 0.,
},
}
}
#[allow(unused)]
pub fn point_normal(&self, time: f32) -> (Point, Vector) {
match self {
Self::Line(_, line) => {
let dir = line.b - line.a;
let p = line.a + dir * time;
let n = normal(line.a, line.b);
(p, n)
}
Self::Curve(_, curve) => {
let p = curve.evaluate(time);
let a = curve.evaluate(time - 0.05);
let b = curve.evaluate(time + 0.05);
let n = normal(a, b);
(p, n)
}
Self::End(..) => (Point::ZERO, Vector::ZERO),
}
}
}
impl Default for Segment {
fn default() -> Self {
Self::End(false)
}
}
// This large epsilon trades fidelity for performance, visual continuity
// and numeric stability.
const MERGE_EPSILON: f32 = 0.01;
/// Creates a segment iterator from a command iterator, optionally producing
/// simplified curves.
pub fn segments<I>(commands: I, simplify_curves: bool) -> Segments<I>
where
I: Iterator + Clone,
I::Item: Borrow<Command>,
{
Segments::new(simplify_curves, commands)
}
/// Iterator over path segments.
#[derive(Clone)]
pub struct Segments<I> {
commands: I,
start: Vector,
prev: Vector,
close: bool,
split: bool,
splits: [Curve; 16],
split_count: usize,
split_index: usize,
last_was_end: bool,
id: u8,
count: u32,
}
impl<I> Segments<I>
where
I: Iterator + Clone,
I::Item: Borrow<Command>,
{
fn new(split: bool, commands: I) -> Self {
Self {
commands,
start: Vector::ZERO,
prev: Vector::ZERO,
close: false,
split,
splits: [Curve::default(); 16],
split_count: 0,
split_index: 0,
last_was_end: true,
id: 0,
count: 0,
}
}
#[allow(clippy::needless_range_loop)]
fn split_curve(&mut self, id: SegmentId, c: &Curve) -> Option<Segment> {
if c.is_line(MERGE_EPSILON) {
if c.a.nearly_eq_by(c.d, MERGE_EPSILON) {
return None;
}
return Some(Segment::Line(id, Line::new(c.a, c.d)));
}
let mut splits = [Curve::default(); 4];
let count = c.split_at_max_curvature(&mut splits);
let mut i = 0;
for j in 0..count {
let curve = splits[j];
if curve.needs_split() {
let (a, b) = curve.split(0.5);
if a.needs_split() {
let (c, d) = a.split(0.5);
self.splits[i] = c;
self.splits[i + 1] = d;
i += 2;
} else {
self.splits[i] = a;
i += 1;
}
if b.needs_split() {
let (c, d) = b.split(0.5);
self.splits[i] = c;
self.splits[i + 1] = d;
i += 2;
} else {
self.splits[i] = b;
i += 1;
}
} else {
self.splits[i] = curve;
i += 1;
}
}
self.split_count = i;
self.split_index = 1;
self.splits[0].to_segment(id)
}
fn inc_id(&mut self) {
if self.id == 254 {
self.id = 0;
} else {
self.id += 1;
}
}
}
impl<I> Iterator for Segments<I>
where
I: Iterator + Clone,
I::Item: Borrow<Command>,
{
type Item = Segment;
fn next(&mut self) -> Option<Self::Item> {
use Command::*;
if self.close {
self.close = false;
self.last_was_end = true;
return Some(Segment::End(true));
}
if self.split {
loop {
if self.split_index < self.split_count {
let curve = self.splits[self.split_index];
self.split_index += 1;
if let Some(segment) = curve.to_segment(self.id) {
self.count += 1;
self.last_was_end = false;
self.prev = curve.d;
return Some(segment);
}
continue;
}
self.inc_id();
let id = self.id;
let from = self.prev;
match *self.commands.next()?.borrow() {
MoveTo(to) => {
self.start = to;
self.prev = to;
self.count = 0;
if !self.last_was_end {
self.last_was_end = true;
return Some(Segment::End(false));
}
}
LineTo(to) => {
if !from.nearly_eq_by(to, MERGE_EPSILON) {
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(Segment::Line(id, Line::new(from, to)));
}
}
CurveTo(c1, c2, to) => {
if let Some(segment) = self.split_curve(id, &Curve::new(from, c1, c2, to)) {
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(segment);
}
}
QuadTo(c, to) => {
if let Some(segment) =
self.split_curve(id, &Curve::from_quadratic(from, c, to))
{
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(segment);
}
}
Close => {
self.prev = self.start;
if self.count == 0 || !from.nearly_eq_by(self.start, MERGE_EPSILON) {
self.close = true;
return Some(Segment::Line(id, Line::new(from, self.start)));
} else {
self.count = 0;
self.last_was_end = true;
return Some(Segment::End(true));
}
}
}
}
} else {
let id = self.id;
self.inc_id();
loop {
let from = self.prev;
match *self.commands.next()?.borrow() {
MoveTo(to) => {
self.start = to;
self.prev = to;
self.count = 0;
if !self.last_was_end {
self.last_was_end = true;
return Some(Segment::End(false));
}
}
LineTo(to) => {
if !from.nearly_eq_by(to, MERGE_EPSILON) {
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(Segment::Line(id, Line::new(from, to)));
}
}
CurveTo(c1, c2, to) => {
let segment = Segment::Curve(id, Curve::new(from, c1, c2, to));
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(segment);
}
QuadTo(c, to) => {
let segment = Segment::Curve(id, Curve::from_quadratic(from, c, to));
self.count += 1;
self.prev = to;
self.last_was_end = false;
return Some(segment);
}
Close => {
self.prev = self.start;
if self.count == 0 || !from.nearly_eq_by(self.start, 0.01) {
self.close = true;
return Some(Segment::Line(id, Line::new(from, self.start)));
} else {
self.count = 0;
self.last_was_end = true;
return Some(Segment::End(true));
}
}
}
}
}
}
}

853
vendor/zeno/src/stroke.rs vendored Normal file
View File

@@ -0,0 +1,853 @@
//! Stroking and dashing of paths.
#![allow(clippy::needless_lifetimes)]
use super::command::Command;
use super::geometry::*;
use super::path_builder::*;
use super::segment::*;
use super::style::*;
#[allow(unused)]
use super::F32Ext;
use crate::lib::Vec;
use core::borrow::Borrow;
pub fn stroke_into<'a, I>(commands: I, style: &Stroke<'a>, sink: &mut impl PathBuilder)
where
I: Iterator + Clone,
I::Item: Borrow<Command>,
{
let mut stroker = Stroker::new(segments(commands, true), sink, style);
let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset);
let mut segment_buf = SmallBuf::new();
if !dashes.is_empty() {
stroker.dash(&mut segment_buf, dashes, dash_offset, empty_gaps);
} else {
stroker.stroke(&mut segment_buf);
}
}
pub fn stroke_with_storage<'a, I>(
commands: I,
style: &Stroke<'a>,
sink: &mut impl PathBuilder,
storage: &mut impl StrokerStorage,
) where
I: Iterator + Clone,
I::Item: Borrow<Command>,
{
let mut stroker = Stroker::new(segments(commands, true), sink, style);
let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset);
if !dashes.is_empty() {
stroker.dash(storage, dashes, dash_offset, empty_gaps);
} else {
stroker.stroke(storage);
}
}
pub struct Stroker<'a, I, S> {
source: Segments<I>,
sink: &'a mut S,
radius: f32,
radius_abs: f32,
join: Join,
inv_miter_limit: f32,
start_cap: Cap,
end_cap: Cap,
}
impl<'a, I, S> Stroker<'a, I, S>
where
I: Iterator + Clone,
I::Item: Borrow<Command>,
S: PathBuilder,
{
pub(super) fn new(source: Segments<I>, sink: &'a mut S, style: &Stroke) -> Self {
let radius = style.width.max(0.01) * 0.5;
Self {
source,
sink,
radius,
radius_abs: radius.abs(),
join: style.join,
inv_miter_limit: if style.miter_limit >= 1. {
1. / style.miter_limit
} else {
1.
},
start_cap: style.start_cap,
end_cap: style.end_cap,
}
}
fn stroke(&mut self, segment_buf: &mut impl StrokerStorage) {
loop {
let (closed, done) = segment_buf.collect(&mut self.source);
self.stroke_segments(segment_buf.get(), closed);
if done {
break;
}
}
}
fn stroke_segments(&mut self, segments: &[Segment], is_closed: bool) {
let len = segments.len();
if len == 0 {
return;
}
if len == 1
&& segments[0].length() == 0.
&& (self.start_cap != Cap::Butt || self.end_cap != Cap::Butt)
{
let segment = segments[0];
let from = match &segment {
Segment::Line(_, line) => line.a,
Segment::Curve(_, curve) => curve.a,
Segment::End(..) => Point::ZERO,
};
let n = Vector::new(0., 1.);
let nr = n * self.radius;
let start = from + nr;
let rstart = from - nr;
self.sink.move_to(start);
self.add_end_cap(start, rstart, n);
self.add_start_cap(rstart, start, n * -1.);
return;
}
let radius = self.radius;
let mut last_dir = Vector::ZERO;
let mut first_point = Point::ZERO;
let mut last_point = Point::ZERO;
let mut pivot = Point::ZERO;
let mut last_id = 0xFF;
if is_closed {
let segment = segments[len - 1].offset(radius);
let end_point = segment.end;
let out_dir = segment.end_normal;
pivot = segment.end_pivot;
last_dir = out_dir;
last_point = end_point;
first_point = end_point;
self.sink.move_to(last_point);
}
// Forward for the outer stroke.
let mut is_first = !is_closed;
for segment in segments {
let segment = segment.offset(radius);
let id = segment.id;
let start = segment.start;
if is_first {
self.sink.move_to(start);
first_point = start;
is_first = false;
} else {
self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
}
last_id = id;
last_dir = segment.end_normal;
pivot = segment.end_pivot;
last_point = self.emit(&segment.segment);
}
// Now backward for the inner stroke.
is_first = true;
for segment in segments.iter().rev() {
let segment = segment.reverse().offset(radius);
let id = segment.id;
let start = segment.start;
if is_first {
if is_closed {
let init = segments[0].reverse().offset(self.radius);
last_point = init.end;
last_dir = init.end_normal;
pivot = init.end_pivot;
self.sink.line_to(init.end);
self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
} else {
self.add_end_cap(last_point, start, last_dir);
}
is_first = false;
} else if id != last_id {
self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
} else {
self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
};
last_id = id;
last_dir = segment.end_normal;
pivot = segment.end_pivot;
last_point = self.emit(&segment.segment);
}
if !is_closed {
self.add_start_cap(last_point, first_point, last_dir);
}
self.sink.close();
}
#[allow(clippy::field_reassign_with_default)]
fn dash(
&mut self,
segment_buf: &mut impl StrokerStorage,
dashes: &[f32],
offset: f32,
empty_gaps: bool,
) {
let mut dasher = Dasher::default();
dasher.empty_gaps = empty_gaps;
let mut done = false;
while !done {
let (is_closed, is_done) = segment_buf.collect(&mut self.source);
done = is_done;
let segments = segment_buf.get();
if segments.is_empty() {
continue;
}
dasher.init(is_closed, dashes, offset);
loop {
match dasher.next(segments, dashes) {
DashOp::Done => break,
DashOp::Continue => {}
DashOp::Emit => {
let (start, end) = dasher.range;
let (t0, t1) = dasher.trange;
self.dash_segments(segments, start, end, t0, t1);
}
DashOp::Stroke => {
self.stroke_segments(segments, true);
break;
}
}
}
}
}
fn dash_segments(&mut self, segments: &[Segment], start: isize, end: isize, t0: f32, t1: f32) {
let radius = self.radius;
if t0 == t1 && start == end {
if self.start_cap == Cap::Butt && self.end_cap == Cap::Butt {
return;
}
let (t0, t1) = if t0 >= 1. {
(t0 - 0.001, t0)
} else {
(t0, t0 + 0.001)
};
let segment = get_signed(segments, start).slice(t0, t1).offset(radius);
let start = segment.start;
let rstart = segment.start - (segment.start_normal * (2. * radius));
self.sink.move_to(start);
self.add_end_cap(start, rstart, segment.start_normal);
self.add_start_cap(rstart, start, segment.start_normal * -1.);
self.sink.close();
return;
}
let mut last_dir = Vector::ZERO;
let mut first_point = Point::ZERO;
let mut last_point = Point::ZERO;
let mut pivot = Point::ZERO;
let mut is_first = true;
let mut last_id = 0xFF;
for i in start..=end {
let t0 = if i == start { t0 } else { 0. };
let t1 = if i == end { t1 } else { 1. };
if t0 >= 1. {
continue;
}
let segment = get_signed(segments, i).slice(t0, t1).offset(radius);
let id = segment.id;
let start = segment.start;
if is_first {
self.sink.move_to(start);
first_point = start;
is_first = false;
} else if id != last_id {
self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
} else {
self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
};
last_id = id;
pivot = segment.end_pivot;
last_dir = segment.end_normal;
last_point = self.emit(&segment.segment);
}
is_first = true;
last_id = 0xFF;
for i in (start..=end).rev() {
let t0 = if i == start { t0 } else { 0. };
let t1 = if i == end { t1 } else { 1. };
if t0 >= 1. {
continue;
}
let segment = get_signed(segments, i)
.slice(t0, t1)
.reverse()
.offset(radius);
let id = segment.id;
let start = segment.start;
if is_first {
self.add_end_cap(last_point, start, last_dir);
is_first = false;
} else if id != last_id {
self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
} else {
self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
};
last_id = id;
pivot = segment.end_pivot;
last_dir = segment.end_normal;
last_point = self.emit(&segment.segment);
}
self.add_start_cap(last_point, first_point, last_dir);
self.sink.close();
}
#[inline(always)]
fn emit(&mut self, segment: &Segment) -> Point {
match segment {
Segment::Line(_, line) => {
self.sink.line_to(line.b);
line.b
}
Segment::Curve(_, curve) => {
self.sink.curve_to(curve.b, curve.c, curve.d);
curve.d
}
_ => Point::ZERO,
}
}
fn add_join(
&mut self,
from: Point,
to: Point,
pivot: Point,
from_normal: Vector,
to_normal: Vector,
) -> Point {
if from.nearly_eq(to) {
return from;
}
if !is_clockwise(from_normal, to_normal) {
self.sink.line_to(pivot);
self.sink.line_to(to);
return to;
}
match self.join {
Join::Bevel => {
self.sink.line_to(to);
to
}
Join::Round => {
let r = self.radius_abs;
let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
arc(self.sink, from, r, r, 0., size, sweep, to);
to
}
Join::Miter => {
let inv_limit = self.inv_miter_limit;
let dot = from_normal.dot(to_normal);
let sin_half = ((1. + dot) * 0.5).sqrt();
if dot < 0.0 || sin_half < inv_limit {
self.sink.line_to(to);
to
} else {
let mid = (from_normal + to_normal).normalize() * (self.radius / sin_half);
let p = pivot + mid;
self.sink.line_to(p);
self.sink.line_to(to);
to
}
}
}
}
fn add_split_join(
&mut self,
from: Point,
to: Point,
pivot: Point,
from_normal: Vector,
to_normal: Vector,
) -> Point {
if from.nearly_eq(to) {
return from;
}
if !is_clockwise(from_normal, to_normal) {
self.sink.line_to(pivot);
self.sink.line_to(to);
return to;
}
let r = self.radius_abs;
let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
arc(self.sink, from, r, r, 0., size, sweep, to);
to
}
fn add_cap(&mut self, from: Point, to: Point, dir: Vector, cap: Cap) {
match cap {
Cap::Butt => {
self.sink.line_to(to);
}
Cap::Square => {
let dir = Vector::new(-dir.y, dir.x);
self.sink.line_to(from + dir * self.radius_abs);
self.sink.line_to(to + dir * self.radius_abs);
self.sink.line_to(to);
}
Cap::Round => {
let r = self.radius_abs;
let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
arc(self.sink, from, r, r, 0., size, sweep, to);
}
}
}
fn add_start_cap(&mut self, from: Point, to: Point, dir: Vector) {
self.add_cap(from, to, dir, self.start_cap);
}
fn add_end_cap(&mut self, from: Point, to: Point, dir: Vector) {
self.add_cap(from, to, dir, self.end_cap);
}
}
enum DashOp {
Done,
Continue,
Emit,
Stroke,
}
#[derive(Copy, Clone, Default)]
struct Dasher {
done: bool,
is_closed: bool,
empty_gaps: bool,
on: bool,
cur: isize,
t0: f32,
t0_offset: f32,
index: usize,
is_first: bool,
first_on: bool,
first_dash: f32,
is_dot: bool,
range: (isize, isize),
trange: (f32, f32),
}
impl Dasher {
fn init(&mut self, is_closed: bool, dashes: &[f32], offset: f32) {
self.done = false;
self.is_closed = is_closed;
self.on = true;
self.cur = 0;
self.t0 = 0.;
self.t0_offset = 0.;
self.index = 0;
self.is_first = true;
self.first_on = true;
let mut first_dash = self.next_dash(dashes);
if offset > 0. {
let mut accum = first_dash;
while accum < offset {
self.on = !self.on;
accum += self.next_dash(dashes);
}
self.first_on = self.on;
first_dash = accum - offset;
}
self.first_dash = first_dash;
}
#[inline(always)]
fn next_dash(&mut self, dashes: &[f32]) -> f32 {
let len = dashes.len();
let mut dash = dashes[self.index % len];
if self.on && self.empty_gaps {
loop {
let next_dash = dashes[(self.index + 1) % len];
if next_dash != 0. {
break;
}
self.index += 2;
dash += dashes[self.index % len];
}
}
self.index += 1;
dash
}
#[inline(always)]
fn next_segments(
dash: f32,
segments: &[Segment],
limit: isize,
start: isize,
start_offset: f32,
) -> (bool, isize, f32, f32) {
let mut cur = start;
let mut goal = dash + start_offset;
let mut segment = get_signed(segments, cur);
loop {
let td = segment.time(goal, 1.);
let dist = td.distance;
let t2 = td.time;
goal -= dist;
if goal <= 0. {
return (true, cur, dist, t2);
}
if cur + 1 >= limit {
return (false, cur, dist, t2);
}
cur += 1;
segment = get_signed(segments, cur);
}
}
#[inline(always)]
fn next(&mut self, segments: &[Segment], dashes: &[f32]) -> DashOp {
if self.done {
return DashOp::Done;
}
let first = self.is_first;
let first_and_closed = first && self.is_closed;
let mut dash = if first {
self.first_dash
} else {
self.next_dash(dashes)
};
let mut on = self.on;
let mut start = self.cur;
let limit = segments.len() as isize;
if self.t0 == 1. && start < limit - 1 {
start += 1;
self.t0 = 0.;
self.t0_offset = 0.;
self.cur = start;
}
let (cont, mut end, mut t1_offset, mut t1) = if dash == 0. {
(true, start, self.t0_offset, self.t0)
} else {
Self::next_segments(dash, segments, limit, start, self.t0_offset)
};
if !cont {
self.done = true;
}
// This is tricky. If the subpath is closed and the last dash is
// "on" we need to join the last dash to the first. Otherwise, we
// need to go back and produce the initial dash that was skipped
// in anticipation of joining to the final dash.
if self.done && self.is_closed {
if on {
// Recompute the final dash including the first.
if first_and_closed {
// The first dash consumed the whole path: emit a single stroke.
return DashOp::Stroke;
}
if self.first_on {
self.cur = start - limit;
start = self.cur;
let (_, end2, end_offset, end_t) =
Self::next_segments(self.first_dash, segments, limit, 0, 0.);
end = end2;
t1_offset = end_offset;
t1 = end_t;
}
} else {
// Emit the first dash.
if !self.first_on {
return DashOp::Done;
}
dash = self.first_dash;
self.cur = 0;
self.t0 = 0.;
self.t0_offset = 0.;
self.on = true;
on = true;
start = self.cur;
let (_, end2, end_offset, end_t) =
Self::next_segments(self.first_dash, segments, limit, 0, 0.);
end = end2;
t1_offset = end_offset;
t1 = end_t;
}
} else if self.done && !on {
return DashOp::Done;
}
self.is_dot = dash == 0.;
let t0 = self.t0;
self.is_first = false;
self.cur = end;
self.t0 = t1;
self.t0_offset = t1_offset;
self.on = !self.on;
if on && !first_and_closed {
self.range = (start, end);
self.trange = (t0, t1);
return DashOp::Emit;
}
DashOp::Continue
}
}
fn validate_dashes(dashes: &[f32], offset: f32) -> (&[f32], f32, bool) {
let len = dashes.len();
if len > 0 {
// Generate a full stroke under any of the following conditions:
// 1. The array contains any negative values.
// 2. All dashes are less than 1 unit.
// 3. All gap dashes are less than 1 unit.
let mut small_count = 0;
let mut gap_sum = 0.;
let mut empty_gaps = false;
let is_odd = len & 1 != 0;
for (i, dash) in dashes.iter().enumerate() {
let is_gap = i & 1 == 1;
if *dash < 1. {
small_count += 1;
if *dash < 0. {
return (&[], 0., false);
} else if *dash == 0. && (is_gap || is_odd) {
empty_gaps = true;
}
} else if is_gap {
gap_sum += *dash;
}
}
if dashes.len() == 1 {
gap_sum = 1.;
}
if small_count < dashes.len() && gap_sum > 0. {
let offset = if offset != 0. {
let mut s: f32 = dashes.iter().sum();
if is_odd {
s *= 2.;
}
if offset < 0. {
s - (offset.abs() % s)
} else {
offset % s
}
} else {
0.
};
return (dashes, offset, empty_gaps);
}
}
(&[], 0., false)
}
#[inline(always)]
fn get_signed(segments: &[Segment], index: isize) -> Segment {
let index = if index < 0 {
segments.len() - (-index) as usize
} else {
index as usize
};
segments[index]
}
fn is_clockwise(a: Vector, b: Vector) -> bool {
a.x * b.y > a.y * b.x
}
impl Segment {
fn offset(&self, radius: f32) -> OffsetSegment {
OffsetSegment::new(self, radius)
}
}
#[derive(Copy, Clone)]
pub struct OffsetSegment {
pub segment: Segment,
pub id: u8,
pub start: Point,
pub end: Point,
pub start_normal: Vector,
pub end_normal: Vector,
pub end_pivot: Point,
}
impl OffsetSegment {
fn new(segment: &Segment, radius: f32) -> Self {
match segment {
Segment::Line(id, Line { a, b }) => {
let n = normal(*a, *b);
let nr = n * radius;
let start = *a + nr;
let end = *b + nr;
Self {
segment: Segment::Line(*id, Line { a: start, b: end }),
id: *id,
start,
end,
start_normal: n,
end_normal: n,
end_pivot: *b,
}
}
Segment::Curve(id, c) => {
const EPS: f32 = 0.5;
//const EPS: f32 = CURVE_EPSILON;
let normal_ab = if c.a.nearly_eq_by(c.b, EPS) {
if c.a.nearly_eq_by(c.c, EPS) {
normal(c.a, c.d)
} else {
normal(c.a, c.c)
}
} else {
normal(c.a, c.b)
};
let normal_bc = if c.b.nearly_eq_by(c.c, EPS) {
if c.b.nearly_eq_by(c.d, EPS) {
normal(c.a, c.d)
} else {
normal(c.b, c.d)
}
} else {
normal(c.b, c.c)
};
let normal_cd = if c.c.nearly_eq_by(c.d, EPS) {
if c.b.nearly_eq_by(c.d, EPS) {
normal(c.a, c.d)
} else {
normal(c.b, c.d)
}
} else {
normal(c.c, c.d)
};
let mut normal_b = normal_ab + normal_bc;
let mut normal_c = normal_cd + normal_bc;
let dot = normal_ab.dot(normal_bc);
normal_b = normal_b.normalize() * (radius / ((1. + dot) * 0.5).sqrt());
let dot = normal_cd.dot(normal_bc);
normal_c = normal_c.normalize() * (radius / ((1. + dot) * 0.5).sqrt());
let start = c.a + normal_ab * radius;
let end = c.d + normal_cd * radius;
Self {
segment: Segment::Curve(
*id,
Curve::new(start, c.b + normal_b, c.c + normal_c, end),
),
id: *id,
start,
end,
start_normal: normal_ab,
end_normal: normal_cd,
end_pivot: c.d,
}
}
Segment::End(..) => Self {
segment: *segment,
id: 0,
start: Point::ZERO,
end: Point::ZERO,
start_normal: Vector::ZERO,
end_normal: Vector::ZERO,
end_pivot: Point::ZERO,
},
}
}
}
pub trait StrokerStorage {
fn clear(&mut self);
fn push(&mut self, segment: &Segment);
fn get(&self) -> &[Segment];
fn collect(&mut self, segments: &mut impl Iterator<Item = Segment>) -> (bool, bool) {
self.clear();
let mut is_closed = false;
let mut done = false;
loop {
if let Some(segment) = segments.next() {
match segment {
Segment::End(closed) => {
is_closed = closed;
break;
}
_ => self.push(&segment),
}
} else {
done = true;
break;
}
}
(is_closed, done)
}
}
impl StrokerStorage for SmallBuf<Segment> {
fn clear(&mut self) {
self.clear();
}
#[inline(always)]
fn push(&mut self, segment: &Segment) {
self.push(*segment);
}
fn get(&self) -> &[Segment] {
self.data()
}
}
impl StrokerStorage for Vec<Segment> {
fn clear(&mut self) {
self.clear();
}
#[inline(always)]
fn push(&mut self, segment: &Segment) {
self.push(*segment);
}
fn get(&self) -> &[Segment] {
self
}
}
const MAX_SMALL_BUF: usize = 128;
#[derive(Clone)]
enum SmallBuf<T> {
Array([T; MAX_SMALL_BUF], usize),
Vec(Vec<T>),
}
impl<T: Copy + Default> SmallBuf<T> {
pub fn new() -> Self {
Self::Array([T::default(); MAX_SMALL_BUF], 0)
}
pub fn data(&self) -> &[T] {
match self {
Self::Array(ref buf, len) => &buf[..*len],
Self::Vec(ref buf) => buf,
}
}
pub fn push(&mut self, value: T) {
match self {
Self::Vec(ref mut buf) => buf.push(value),
Self::Array(ref mut buf, ref mut len) => {
if *len == MAX_SMALL_BUF {
let mut vec = Vec::from(&buf[..]);
vec.push(value);
*self = Self::Vec(vec);
} else {
buf[*len] = value;
*len += 1;
}
}
}
}
pub fn clear(&mut self) {
match self {
Self::Array(_, ref mut len) => *len = 0,
Self::Vec(ref mut buf) => buf.clear(),
}
}
}

172
vendor/zeno/src/style.rs vendored Normal file
View File

@@ -0,0 +1,172 @@
//! Path styles.
/// Describes the visual style of a fill.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Fill {
/// The non-zero fill rule.
NonZero,
/// The even-odd fill rule.
EvenOdd,
}
/// Defines the connection between two segments of a stroke.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Join {
/// A straight line connecting the segments.
Bevel,
/// The segments are extended to their natural intersection point.
Miter,
/// An arc between the segments.
Round,
}
/// Defines the shape to be drawn at the beginning or end of a stroke.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Cap {
/// Flat cap.
Butt,
/// Square cap with dimensions equal to half the stroke width.
Square,
/// Rounded cap with radius equal to half the stroke width.
Round,
}
/// Describes the visual style of a stroke.
#[derive(Copy, Clone, Debug)]
pub struct Stroke<'a> {
/// Width of the stroke.
pub width: f32,
/// Style for connecting segments of the stroke.
pub join: Join,
/// Limit for miter joins.
pub miter_limit: f32,
/// Style for capping the beginning of an open subpath.
pub start_cap: Cap,
/// Style for capping the end of an open subpath.
pub end_cap: Cap,
/// Lengths of dashes in alternating on/off order.
pub dashes: &'a [f32],
/// Offset of the first dash.
pub offset: f32,
/// True if the stroke width should be affected by the scale of a transform.
pub scale: bool,
}
impl Default for Stroke<'_> {
fn default() -> Self {
Self {
width: 1.,
join: Join::Miter,
miter_limit: 4.,
start_cap: Cap::Butt,
end_cap: Cap::Butt,
dashes: &[],
offset: 0.,
scale: true,
}
}
}
impl<'a> Stroke<'a> {
/// Creates a new stroke style with the specified width.
#[allow(clippy::field_reassign_with_default)]
pub fn new(width: f32) -> Self {
let mut s = Self::default();
s.width = width;
s
}
/// Sets the width of the stroke. The default is 1.
pub fn width(&mut self, width: f32) -> &mut Self {
self.width = width;
self
}
/// Sets the join style that determines how individual segments of the path
/// will be connected. The default is miter.
pub fn join(&mut self, join: Join) -> &mut Self {
self.join = join;
self
}
/// Sets the limit for miter joins beyond which a bevel will be generated.
/// The default is 4.
pub fn miter_limit(&mut self, limit: f32) -> &mut Self {
self.miter_limit = limit;
self
}
/// Sets the cap style that will be generated at the start and end of the
/// stroke. Note that this will override the individual start and end cap
/// options. The default is butt.
pub fn cap(&mut self, cap: Cap) -> &mut Self {
self.start_cap = cap;
self.end_cap = cap;
self
}
/// Sets both the start and end cap styles for the stroke.
pub fn caps(&mut self, start: Cap, end: Cap) -> &mut Self {
self.start_cap = start;
self.end_cap = end;
self
}
/// Sets the dash array and offset of the stroke. The default is an empty
/// array, meaning that the stroke will be drawn as a continuous line.
pub fn dash(&mut self, dashes: &'a [f32], offset: f32) -> &mut Self {
self.dashes = dashes;
self.offset = offset;
self
}
/// Sets whether or not scaling is applied to the stroke. The default is true.
pub fn scale(&mut self, scale: bool) -> &mut Self {
self.scale = scale;
self
}
}
/// Represents the style of a path for rendering or hit testing.
#[derive(Copy, Clone, Debug)]
pub enum Style<'a> {
Fill(Fill),
Stroke(Stroke<'a>),
}
impl Default for Style<'_> {
fn default() -> Self {
Self::Fill(Fill::NonZero)
}
}
impl Style<'_> {
/// Returns true if the style is a stroke.
pub fn is_stroke(&self) -> bool {
matches!(self, Self::Stroke(_))
}
}
impl From<Fill> for Style<'_> {
fn from(style: Fill) -> Self {
Self::Fill(style)
}
}
impl<'a> From<Stroke<'a>> for Style<'a> {
fn from(style: Stroke<'a>) -> Self {
Self::Stroke(style)
}
}
impl<'a> From<&'a Stroke<'a>> for Style<'a> {
fn from(style: &'a Stroke<'a>) -> Self {
Self::Stroke(*style)
}
}
impl<'a> From<&'a mut Stroke<'a>> for Style<'a> {
fn from(style: &'a mut Stroke<'a>) -> Self {
Self::Stroke(*style)
}
}

659
vendor/zeno/src/svg_parser.rs vendored Normal file
View File

@@ -0,0 +1,659 @@
//! SVG path data parser.
use super::command::Command;
use super::geometry::Vector;
use super::path_builder::{Arc, ArcSize, ArcSweep};
#[derive(Copy, Clone)]
enum State {
Initial,
Next,
Continue(u8),
}
#[derive(Clone)]
pub struct SvgCommands<'a> {
buf: &'a [u8],
cur: u8,
pub pos: usize,
cmd_pos: usize,
pub error: bool,
pub done: bool,
start_point: Vector,
cur_point: Vector,
last_control: Vector,
last_cmd: u8,
state: State,
arc: Arc,
}
impl Iterator for SvgCommands<'_> {
type Item = Command;
fn next(&mut self) -> Option<Self::Item> {
self.parse()
}
}
impl<'a> SvgCommands<'a> {
pub(crate) fn new(source: &'a str) -> Self {
Self {
buf: source.as_bytes(),
cur: 0,
pos: 0,
cmd_pos: 0,
error: false,
done: false,
start_point: Vector::ZERO,
cur_point: Vector::ZERO,
last_control: Vector::ZERO,
last_cmd: 0,
state: State::Initial,
arc: Arc::default(),
}
}
fn parse(&mut self) -> Option<Command> {
use Command::*;
let mut cmd = self.cur;
loop {
if let Some(cmd) = self.arc.next() {
return Some(cmd);
}
self.last_cmd = cmd;
match self.state {
State::Initial => {
self.advance();
self.skip_whitespace();
self.state = State::Next;
continue;
}
State::Next => {
self.skip_whitespace();
self.cmd_pos = self.pos;
cmd = self.cur;
self.advance();
self.skip_whitespace();
self.state = State::Continue(cmd);
match cmd {
b'z' | b'Z' => {
self.state = State::Next;
self.cur_point = self.start_point;
return Some(Close);
}
b'M' => {
let to = self.point_to()?;
self.start_point = to;
self.skip_comma_whitespace();
return Some(MoveTo(to));
}
b'm' => {
let to = self.rel_point_to()?;
self.start_point = to;
self.skip_comma_whitespace();
return Some(MoveTo(to));
}
b'L' => {
let to = self.point_to()?;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'l' => {
let to = self.rel_point_to()?;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'H' => {
let x = self.coord()?;
let to = Vector::new(x, self.cur_point.y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'h' => {
let x = self.coord()?;
let to = Vector::new(self.cur_point.x + x, self.cur_point.y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'V' => {
let y = self.coord()?;
let to = Vector::new(self.cur_point.x, y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'v' => {
let y = self.coord()?;
let to = Vector::new(self.cur_point.x, self.cur_point.y + y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
}
b'C' => {
let (c1, c2, to) = self.three_points_to()?;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
}
b'c' => {
let (c1, c2, to) = self.rel_three_points_to()?;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
}
b'S' => {
let (c2, to) = self.two_points()?;
let c1 = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
}
b's' => {
let (c2, to) = self.rel_two_points()?;
let c1 = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
}
b'Q' => {
let (c, to) = self.two_points_to()?;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
}
b'q' => {
let (c, to) = self.rel_two_points_to()?;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
}
b'T' => {
let to = self.point()?;
let c = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
}
b't' => {
let to = self.rel_point()?;
let c = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
}
b'A' => {
let from = self.cur_point;
let (rx, ry, a, size, sweep, to) = self.arc_arguments(false)?;
self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
self.skip_comma_whitespace();
continue;
}
b'a' => {
let from = self.cur_point;
let (rx, ry, a, size, sweep, to) = self.arc_arguments(true)?;
self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
self.skip_comma_whitespace();
continue;
}
_ => {
if !self.done || cmd != 0 {
self.error = true;
self.pos = self.cmd_pos;
}
return None;
}
}
}
State::Continue(cmd) => match cmd {
b'M' => {
if let Some(to) = self.point_to() {
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'm' => {
if let Some(to) = self.rel_point_to() {
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'L' => {
if let Some(to) = self.point_to() {
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'l' => {
if let Some(to) = self.rel_point_to() {
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'H' => {
if let Some(x) = self.coord() {
let to = Vector::new(x, self.cur_point.y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'h' => {
if let Some(x) = self.coord() {
let to = Vector::new(self.cur_point.x + x, self.cur_point.y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'V' => {
if let Some(y) = self.coord() {
let to = Vector::new(self.cur_point.x, y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'v' => {
if let Some(y) = self.coord() {
let to = Vector::new(self.cur_point.x, self.cur_point.y + y);
self.cur_point = to;
self.skip_comma_whitespace();
return Some(LineTo(to));
} else {
self.state = State::Next;
}
}
b'C' => {
if let Some(c1) = self.point() {
self.skip_comma_whitespace();
let (c2, to) = self.two_points_to()?;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
} else {
self.state = State::Next;
}
}
b'c' => {
if let Some(c1) = self.rel_point() {
self.skip_comma_whitespace();
let (c2, to) = self.rel_two_points_to()?;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
} else {
self.state = State::Next;
}
}
b'S' => {
if let Some(c2) = self.point() {
self.skip_comma_whitespace();
let to = self.point()?;
let c1 = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
} else {
self.state = State::Next;
}
}
b's' => {
if let Some(c2) = self.rel_point() {
self.skip_comma_whitespace();
let to = self.rel_point()?;
let c1 = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c2;
self.skip_comma_whitespace();
return Some(CurveTo(c1, c2, to));
} else {
self.state = State::Next;
}
}
b'Q' => {
if let Some(c) = self.point() {
self.last_control = c;
self.skip_comma_whitespace();
let to = self.point_to()?;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
} else {
self.state = State::Next;
}
}
b'q' => {
if let Some(c) = self.rel_point() {
self.last_control = c;
self.skip_comma_whitespace();
let to = self.rel_point_to()?;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
} else {
self.state = State::Next;
}
}
b'T' => {
if let Some(to) = self.point() {
let c = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
} else {
self.state = State::Next;
}
}
b't' => {
if let Some(to) = self.rel_point() {
let c = self.reflected_control(cmd);
self.cur_point = to;
self.last_control = c;
self.skip_comma_whitespace();
return Some(QuadTo(c, to));
} else {
self.state = State::Next;
}
}
b'A' => {
if let Some(rx) = self.coord() {
let from = self.cur_point;
let (ry, a, size, sweep, to) = self.arc_rest_arguments(false)?;
self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
self.skip_comma_whitespace();
} else {
self.state = State::Next;
}
}
b'a' => {
if let Some(rx) = self.coord() {
let from = self.cur_point;
let (ry, a, size, sweep, to) = self.arc_rest_arguments(true)?;
self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
self.skip_comma_whitespace();
} else {
self.state = State::Next;
}
}
_ => {
if !self.done || cmd != 0 {
self.error = true;
self.pos = self.cmd_pos;
}
return None;
}
},
}
}
}
fn reflected_control(&self, cmd: u8) -> Vector {
let cur = self.cur_point;
let old = self.last_control;
if cmd == b'S' || cmd == b's' {
match self.last_cmd {
b'C' | b'c' | b'S' | b's' => (2. * cur.x - old.x, 2. * cur.y - old.y).into(),
_ => self.cur_point,
}
} else {
match self.last_cmd {
b'Q' | b'q' | b'T' | b't' => (2. * cur.x - old.x, 2. * cur.y - old.y).into(),
_ => self.cur_point,
}
}
}
fn arc_arguments(&mut self, rel: bool) -> Option<(f32, f32, f32, ArcSize, ArcSweep, Vector)> {
let rx = self.coord()?;
self.skip_comma_whitespace();
let ry = self.coord()?;
self.skip_comma_whitespace();
let a = self.coord()?;
self.skip_comma_whitespace();
let large_arc = self.boolean()?;
self.skip_comma_whitespace();
let sweep = self.boolean()?;
self.skip_comma_whitespace();
let to = if rel {
self.rel_point_to()?
} else {
self.point_to()?
};
let size = if large_arc {
ArcSize::Large
} else {
ArcSize::Small
};
let sweep = if sweep {
ArcSweep::Positive
} else {
ArcSweep::Negative
};
Some((rx, ry, a, size, sweep, to))
}
fn arc_rest_arguments(&mut self, rel: bool) -> Option<(f32, f32, ArcSize, ArcSweep, Vector)> {
let ry = self.coord()?;
self.skip_comma_whitespace();
let a = self.coord()?;
self.skip_comma_whitespace();
let large_arc = self.boolean()?;
self.skip_comma_whitespace();
let sweep = self.boolean()?;
self.skip_comma_whitespace();
let to = if rel {
self.rel_point_to()?
} else {
self.point_to()?
};
let size = if large_arc {
ArcSize::Large
} else {
ArcSize::Small
};
let sweep = if sweep {
ArcSweep::Positive
} else {
ArcSweep::Negative
};
Some((ry, a, size, sweep, to))
}
fn point(&mut self) -> Option<Vector> {
let a = self.coord()?;
self.skip_comma_whitespace();
let b = self.coord()?;
Some((a, b).into())
}
fn point_to(&mut self) -> Option<Vector> {
let p = self.point()?;
self.cur_point = p;
Some(p)
}
fn rel_point(&mut self) -> Option<Vector> {
let p = self.point()?;
Some(p + self.cur_point)
}
fn rel_point_to(&mut self) -> Option<Vector> {
let p = self.rel_point()?;
self.cur_point = p;
Some(p)
}
fn two_points_to(&mut self) -> Option<(Vector, Vector)> {
let a = self.point()?;
self.skip_comma_whitespace();
let b = self.point_to()?;
Some((a, b))
}
fn two_points(&mut self) -> Option<(Vector, Vector)> {
let a = self.point()?;
self.skip_comma_whitespace();
let b = self.point()?;
Some((a, b))
}
fn rel_two_points_to(&mut self) -> Option<(Vector, Vector)> {
let a = self.rel_point()?;
self.skip_comma_whitespace();
let b = self.rel_point_to()?;
Some((a, b))
}
fn rel_two_points(&mut self) -> Option<(Vector, Vector)> {
let a = self.rel_point()?;
self.skip_comma_whitespace();
let b = self.rel_point()?;
Some((a, b))
}
fn three_points_to(&mut self) -> Option<(Vector, Vector, Vector)> {
let a = self.point()?;
self.skip_comma_whitespace();
let b = self.point()?;
self.skip_comma_whitespace();
let c = self.point_to()?;
Some((a, b, c))
}
fn rel_three_points_to(&mut self) -> Option<(Vector, Vector, Vector)> {
let a = self.rel_point()?;
self.skip_comma_whitespace();
let b = self.rel_point()?;
self.skip_comma_whitespace();
let c = self.rel_point_to()?;
Some((a, b, c))
}
fn coord(&mut self) -> Option<f32> {
match self.cur {
b'+' => {
self.advance();
self.number()
}
b'-' => {
self.advance();
Some(-self.number()?)
}
_ => self.number(),
}
}
fn number(&mut self) -> Option<f32> {
let mut buf = [0u8; 32];
let mut pos = 0;
let mut has_decimal = false;
loop {
match self.cur {
b'.' => {
if has_decimal {
break;
} else {
*buf.get_mut(pos)? = self.cur;
pos += 1;
has_decimal = true;
}
}
b'0'..=b'9' => {
*buf.get_mut(pos)? = self.cur;
pos += 1;
}
_ => break,
}
self.advance();
}
let s = core::str::from_utf8(&buf[..pos]).ok()?;
s.parse::<f32>().ok()
}
fn boolean(&mut self) -> Option<bool> {
match self.cur {
b'0' => {
self.advance();
Some(false)
}
b'1' => {
self.advance();
Some(true)
}
_ => None,
}
}
fn skip_comma_whitespace(&mut self) {
self.skip_whitespace();
if self.accept(b',') {
self.skip_whitespace();
}
}
#[allow(clippy::match_like_matches_macro)]
fn skip_whitespace(&mut self) {
while self.accept_by(|b| match b {
0x9 | 0x20 | 0xA | 0xC | 0xD => true,
_ => false,
}) {}
}
fn accept(&mut self, b: u8) -> bool {
if self.cur == b {
self.advance();
return true;
}
false
}
fn accept_by(&mut self, f: impl Fn(u8) -> bool) -> bool {
if f(self.cur) {
self.advance();
return true;
}
false
}
fn advance(&mut self) {
if self.pos == self.buf.len() {
self.done = true;
self.cur = 0;
return;
}
self.cur = self.buf[self.pos];
self.pos += 1;
}
}
/// Returns an error indicating the first position of invalid SVG path data.
pub fn validate_svg(svg: &str) -> Result<(), usize> {
let cmds = &mut SvgCommands::new(svg);
cmds.count();
let pos = cmds.pos;
if cmds.error || pos != svg.len() {
Err(pos.saturating_sub(1))
} else {
Ok(())
}
}

235
vendor/zeno/src/traversal.rs vendored Normal file
View File

@@ -0,0 +1,235 @@
//! Path traversal algorithms.
use super::command::{Command, TransformCommands};
use super::geometry::*;
use super::path_data::PathData;
use super::segment::{segments, Segment, Segments};
use core::borrow::Borrow;
use core::cell::RefCell;
/// A vertex of a path.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Vertex {
/// The start point and direction of a subpath.
Start(Point, Vector),
/// The incoming direction, location, and outgoing direction of an
/// intermediate vertex in a subpath.
Middle(Vector, Point, Vector),
/// The incoming direction and location of the final vertex in a subpath.
/// The boolean value is true if the subpath is closed.
End(Vector, Point, bool),
}
/// An iterator over the vertices of a path.
#[derive(Clone)]
pub struct Vertices<D> {
segments: Segments<D>,
prev_point: Point,
prev_dir: Vector,
is_first: bool,
}
impl<D> Vertices<D>
where
D: Iterator<Item = Command> + Clone,
{
/// Creates a new iterator over the vertices of a path.
pub fn new(data: impl PathData<Commands = D>) -> Self {
Self {
segments: segments(data.commands(), false),
prev_point: Point::ZERO,
prev_dir: Vector::new(1., 0.),
is_first: true,
}
}
}
impl<D> Vertices<TransformCommands<D>>
where
D: Iterator<Item = Command> + Clone,
{
/// Creates a new iterator over the vertices of a transformed path.
pub fn with_transform(data: impl PathData<Commands = D>, transform: Transform) -> Self {
let data = TransformCommands {
data: data.commands(),
transform,
};
Self {
segments: segments(data, false),
prev_point: Point::ZERO,
prev_dir: Vector::new(1., 0.),
is_first: true,
}
}
}
impl<D> Iterator for Vertices<D>
where
D: Iterator + Clone,
D::Item: Borrow<Command>,
{
type Item = Vertex;
fn next(&mut self) -> Option<Self::Item> {
use Segment::*;
if self.is_first {
self.is_first = false;
match self.segments.next()?.borrow() {
End(closed) => {
self.is_first = true;
Some(Vertex::End(self.prev_dir, self.prev_point, *closed))
}
segment => {
let (start, in_dir, out_dir, end) = get_components(segment);
self.prev_dir = out_dir;
self.prev_point = end;
Some(Vertex::Start(start, in_dir))
}
}
} else {
match self.segments.next()?.borrow() {
End(closed) => {
self.is_first = true;
Some(Vertex::End(self.prev_dir, self.prev_point, *closed))
}
segment => {
let (start, in_dir, out_dir, end) = get_components(segment);
let prev_dir = self.prev_dir;
self.prev_dir = out_dir;
self.prev_point = end;
Some(Vertex::Middle(prev_dir, start, in_dir))
}
}
}
}
}
fn get_components(segment: &Segment) -> (Point, Vector, Vector, Point) {
match segment {
Segment::Curve(_, curve) => {
let a = curve.evaluate(0.05);
let b = curve.evaluate(0.95);
let a_dir = (a - curve.a).normalize();
let b_dir = (curve.d - b).normalize();
(curve.a, a_dir, b_dir, curve.d)
}
Segment::Line(_, line) => {
let dir = (line.b - line.a).normalize();
(line.a, dir, dir, line.b)
}
Segment::End(..) => (Point::ZERO, Vector::ZERO, Vector::ZERO, Point::ZERO),
}
}
/// An iterator like type that walks along a path by arbitrary steps.
pub struct Walk<D> {
init: Segments<D>,
iter: Segments<D>,
segment: Segment,
segment_offset: f32,
first: bool,
length: RefCell<Option<f32>>,
walked: f32,
}
impl<D> Walk<D>
where
D: Iterator<Item = Command> + Clone,
{
/// Creates a new iterator like type that steps along a path by arbitrary distances.
pub fn new(data: impl PathData<Commands = D>) -> Self {
let data = data.commands();
Self {
init: segments(data.clone(), false),
iter: segments(data, false),
segment: Segment::default(),
segment_offset: 0.,
first: true,
length: RefCell::new(None),
walked: 0.,
}
}
}
impl<D> Walk<TransformCommands<D>>
where
D: Iterator<Item = Command> + Clone,
{
/// Creates a new iterator like type that steps along a transformed path by arbitrary distances.
pub fn with_transform(data: impl PathData<Commands = D>, transform: Transform) -> Self {
let data = data.commands();
let data = TransformCommands { data, transform };
Self {
init: segments(data.clone(), false),
iter: segments(data, false),
segment: Segment::default(),
segment_offset: 0.,
first: true,
length: RefCell::new(None),
walked: 0.,
}
}
}
impl<D> Walk<D>
where
D: Iterator + Clone,
D::Item: Borrow<Command>,
{
/// Steps by the specified distance and returns the point at the new
/// location and the normal vector describing the left-ward direction at
/// that point. Returns `None` if the distance steps beyond the end
/// of the path.
pub fn step(&mut self, distance: f32) -> Option<(Point, Vector)> {
if self.first {
self.segment = self.next_segment()?;
self.segment_offset = 0.;
self.first = false;
}
let mut t;
let mut offset = self.segment_offset;
let mut segment = self.segment;
let mut remaining = distance;
loop {
let dt = segment.time(offset + remaining, 1.);
remaining -= dt.distance - offset;
t = dt.time;
offset = dt.distance;
if remaining <= 0. {
break;
}
segment = self.next_segment()?;
offset = 0.;
}
self.segment = segment;
self.segment_offset = offset;
self.walked += distance;
let (p, n) = segment.point_normal(t);
Some((p, n))
}
/// Returns the remaining distance available to walk on the path.
pub fn remaining(&self) -> f32 {
let mut l = self.length.borrow_mut();
if l.is_none() {
let iter = self.init.clone();
let mut sum = 0.;
for s in iter {
sum += s.length();
}
*l = Some(sum);
}
l.unwrap() - self.walked
}
fn next_segment(&mut self) -> Option<Segment> {
for s in self.iter.by_ref() {
match s {
Segment::End(..) => continue,
_ => return Some(s),
}
}
None
}
}