From 8ff3da8a68abbcb8f691db709c3ea8fd1bb209e4 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 11 Jan 2019 15:24:13 -0800 Subject: [PATCH] Build outlines from segments; move SIMD points into `geometry` --- Cargo.lock | 3 + geometry/Cargo.toml | 4 ++ geometry/src/lib.rs | 8 +-- geometry/src/normals.rs | 2 +- geometry/src/point.rs | 107 +++++++++++++++++++++++++++++ geometry/src/stroke.rs | 3 +- utils/tile-svg/src/main.rs | 135 ++++++++++--------------------------- 7 files changed, 155 insertions(+), 107 deletions(-) create mode 100644 geometry/src/point.rs diff --git a/Cargo.lock b/Cargo.lock index e9d92d1c..bad16a9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,9 +429,12 @@ name = "pathfinder_geometry" version = "0.3.0" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lyon_geom 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "simdeez 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/geometry/Cargo.toml b/geometry/Cargo.toml index fe93bd3a..ccfced40 100644 --- a/geometry/Cargo.toml +++ b/geometry/Cargo.toml @@ -1,10 +1,14 @@ [package] name = "pathfinder_geometry" version = "0.3.0" +edition = "2018" authors = ["Patrick Walton "] [dependencies] arrayvec = "0.4" +euclid = "0.19" +lyon_geom = "0.12" lyon_path = "0.12" serde = "1.0" serde_derive = "1.0" +simdeez = "0.4" diff --git a/geometry/src/lib.rs b/geometry/src/lib.rs index 0dde3fda..cb0d6bee 100644 --- a/geometry/src/lib.rs +++ b/geometry/src/lib.rs @@ -12,16 +12,16 @@ //! //! These may be merged into upstream Lyon eventually. -extern crate arrayvec; -extern crate lyon_path; +use simdeez::sse41::Sse41; -use lyon_path::geom as lyon_geom; -use lyon_path::geom::euclid; +// TODO(pcwalton): Make this configurable. +pub type SimdImpl = Sse41; pub mod clip; pub mod cubic_to_quadratic; pub mod normals; pub mod orientation; +pub mod point; pub mod segments; pub mod stroke; pub mod transform; diff --git a/geometry/src/normals.rs b/geometry/src/normals.rs index dda127a8..6fd5b5e9 100644 --- a/geometry/src/normals.rs +++ b/geometry/src/normals.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::orientation::Orientation; use euclid::approxeq::ApproxEq; use euclid::{Point2D, Vector2D}; use lyon_path::PathEvent; -use orientation::Orientation; #[derive(Clone, Copy, Debug)] pub struct SegmentNormals { diff --git a/geometry/src/point.rs b/geometry/src/point.rs new file mode 100644 index 00000000..e768ae14 --- /dev/null +++ b/geometry/src/point.rs @@ -0,0 +1,107 @@ +// pathfinder/geometry/src/point.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A SIMD-optimized point type. + +use crate::SimdImpl; +use euclid::Point2D; +use simdeez::Simd; +use std::ops::{Add, Mul, Sub}; + +#[derive(Clone, Copy, Debug)] +pub struct Point2DF32(pub ::Vf32); + +impl Point2DF32 { + #[inline] + pub fn new(x: f32, y: f32) -> Point2DF32 { + unsafe { + let mut data = SimdImpl::setzero_ps(); + data[0] = x; + data[1] = y; + Point2DF32(data) + } + } + + #[inline] + pub fn splat(value: f32) -> Point2DF32 { + unsafe { Point2DF32(SimdImpl::set1_ps(value)) } + } + + #[inline] + pub fn from_euclid(point: Point2D) -> Point2DF32 { + Point2DF32::new(point.x, point.y) + } + + #[inline] + pub fn as_euclid(&self) -> Point2D { + Point2D::new(self.0[0], self.0[1]) + } + + #[inline] + pub fn x(&self) -> f32 { + self.0[0] + } + + #[inline] + pub fn y(&self) -> f32 { + self.0[1] + } + + #[inline] + pub fn min(&self, other: Point2DF32) -> Point2DF32 { + unsafe { Point2DF32(SimdImpl::min_ps(self.0, other.0)) } + } + + #[inline] + pub fn max(&self, other: Point2DF32) -> Point2DF32 { + unsafe { Point2DF32(SimdImpl::max_ps(self.0, other.0)) } + } +} + +impl PartialEq for Point2DF32 { + #[inline] + fn eq(&self, other: &Point2DF32) -> bool { + unsafe { + let results = SimdImpl::castps_epi32(SimdImpl::cmpeq_ps(self.0, other.0)); + results[0] == -1 && results[1] == -1 + } + } +} + +impl Default for Point2DF32 { + #[inline] + fn default() -> Point2DF32 { + unsafe { Point2DF32(SimdImpl::setzero_ps()) } + } +} + +impl Add for Point2DF32 { + type Output = Point2DF32; + #[inline] + fn add(self, other: Point2DF32) -> Point2DF32 { + Point2DF32(self.0 + other.0) + } +} + +impl Sub for Point2DF32 { + type Output = Point2DF32; + #[inline] + fn sub(self, other: Point2DF32) -> Point2DF32 { + Point2DF32(self.0 - other.0) + } +} + +impl Mul for Point2DF32 { + type Output = Point2DF32; + #[inline] + fn mul(self, other: Point2DF32) -> Point2DF32 { + Point2DF32(self.0 * other.0) + } +} diff --git a/geometry/src/stroke.rs b/geometry/src/stroke.rs index 505f0137..08e4e702 100644 --- a/geometry/src/stroke.rs +++ b/geometry/src/stroke.rs @@ -10,11 +10,10 @@ //! Utilities for converting path strokes to fills. +use crate::segments::{Segment, SegmentIter}; use lyon_path::PathEvent; use lyon_path::iterator::PathIterator; -use segments::{Segment, SegmentIter}; - #[derive(Clone, Copy, Debug)] pub struct StrokeStyle { pub width: f32, diff --git a/utils/tile-svg/src/main.rs b/utils/tile-svg/src/main.rs index 097d671f..1578d6e3 100644 --- a/utils/tile-svg/src/main.rs +++ b/utils/tile-svg/src/main.rs @@ -27,6 +27,7 @@ use hashbrown::HashMap; use jemallocator; use lyon_path::PathEvent; use lyon_path::iterator::PathIter; +use pathfinder_geometry::point::Point2DF32; use pathfinder_geometry::stroke::{StrokeStyle, StrokeToFillIter}; use rayon::ThreadPoolBuilder; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; @@ -40,7 +41,7 @@ use std::fs::File; use std::io::{self, BufWriter, Write}; use std::iter; use std::mem; -use std::ops::{Add, Mul, Sub}; +use std::ops::Sub; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::time::{Duration, Instant}; @@ -236,8 +237,7 @@ impl Scene { let path = UsvgPathToSegments::new(path.segments.iter().cloned()); let path = PathTransformingIter::new(path, &transform); let path = MonotonicConversionIter::new(path); - let path = SegmentsToPathEvents::new(path); - let outline = Outline::from_path_events(path); + let outline = Outline::from_segments(path); scene.bounds = scene.bounds.union(&outline.bounds); scene.objects.push(PathObject::new(outline, @@ -258,8 +258,7 @@ impl Scene { let path = PathEventsToSegments::new(path); let path = PathTransformingIter::new(path, &transform); let path = MonotonicConversionIter::new(path); - let path = SegmentsToPathEvents::new(path); - let outline = Outline::from_path_events(path); + let outline = Outline::from_segments(path); scene.bounds = scene.bounds.union(&outline.bounds); scene.objects.push(PathObject::new(outline, @@ -350,54 +349,48 @@ impl Outline { } } - // NB: Assumes the path has already been transformed. - fn from_path_events(path_events: I) -> Outline where I: Iterator { + fn from_segments(segments: I) -> Outline where I: Iterator { let mut outline = Outline::new(); let mut current_contour = Contour::new(); let mut bounding_points = None; - for path_event in path_events { - match path_event { - PathEvent::MoveTo(to) => { - if !current_contour.is_empty() { - outline.contours.push(mem::replace(&mut current_contour, Contour::new())) - } - current_contour.push_point(Point2DF32::from_euclid(to), - PointFlags::empty(), - &mut bounding_points); + for segment in segments { + if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) { + if !current_contour.is_empty() { + outline.contours.push(mem::replace(&mut current_contour, Contour::new())); } - PathEvent::LineTo(to) => { - current_contour.push_point(Point2DF32::from_euclid(to), - PointFlags::empty(), - &mut bounding_points); + current_contour.push_point(segment.baseline.from(), + PointFlags::empty(), + &mut bounding_points); + } + + if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) { + if !current_contour.is_empty() { + outline.contours.push(mem::replace(&mut current_contour, Contour::new())); } - PathEvent::QuadraticTo(ctrl, to) => { - current_contour.push_point(Point2DF32::from_euclid(ctrl), - PointFlags::CONTROL_POINT_0, - &mut bounding_points); - current_contour.push_point(Point2DF32::from_euclid(to), - PointFlags::empty(), - &mut bounding_points); - } - PathEvent::CubicTo(ctrl0, ctrl1, to) => { - current_contour.push_point(Point2DF32::from_euclid(ctrl0), - PointFlags::CONTROL_POINT_0, - &mut bounding_points); - current_contour.push_point(Point2DF32::from_euclid(ctrl1), + continue; + } + + if segment.is_none() { + continue; + } + + if !segment.is_line() { + current_contour.push_point(segment.ctrl.from(), + PointFlags::CONTROL_POINT_0, + &mut bounding_points); + if !segment.is_quadratic() { + current_contour.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1, &mut bounding_points); - current_contour.push_point(Point2DF32::from_euclid(to), - PointFlags::empty(), - &mut bounding_points); } - PathEvent::Close => { - if !current_contour.is_empty() { - outline.contours.push(mem::replace(&mut current_contour, Contour::new())); - } - } - PathEvent::Arc(..) => unimplemented!("arcs"), } + + current_contour.push_point(segment.baseline.to(), + PointFlags::empty(), + &mut bounding_points); } + if !current_contour.is_empty() { outline.contours.push(current_contour) } @@ -2179,64 +2172,6 @@ impl PartialOrd for ActiveEdge { // Geometry -#[derive(Clone, Copy, Debug)] -struct Point2DF32(::Vf32); - -impl Point2DF32 { - fn new(x: f32, y: f32) -> Point2DF32 { - unsafe { - let mut data = Sse41::setzero_ps(); - data[0] = x; - data[1] = y; - Point2DF32(data) - } - } - - fn splat(value: f32) -> Point2DF32 { unsafe { Point2DF32(Sse41::set1_ps(value)) } } - - fn from_euclid(point: Point2D) -> Point2DF32 { Point2DF32::new(point.x, point.y) } - fn as_euclid(&self) -> Point2D { Point2D::new(self.0[0], self.0[1]) } - - fn x(&self) -> f32 { self.0[0] } - fn y(&self) -> f32 { self.0[1] } - - fn min(&self, other: Point2DF32) -> Point2DF32 { - unsafe { Point2DF32(Sse41::min_ps(self.0, other.0)) } - } - - fn max(&self, other: Point2DF32) -> Point2DF32 { - unsafe { Point2DF32(Sse41::max_ps(self.0, other.0)) } - } -} - -impl PartialEq for Point2DF32 { - fn eq(&self, other: &Point2DF32) -> bool { - unsafe { - let results: ::Vi32 = mem::transmute(Sse41::cmpeq_ps(self.0, other.0)); - results[0] == -1 && results[1] == -1 - } - } -} - -impl Default for Point2DF32 { - fn default() -> Point2DF32 { unsafe { Point2DF32(Sse41::setzero_ps()) } } -} - -impl Add for Point2DF32 { - type Output = Point2DF32; - fn add(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 + other.0) } -} - -impl Sub for Point2DF32 { - type Output = Point2DF32; - fn sub(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 - other.0) } -} - -impl Mul for Point2DF32 { - type Output = Point2DF32; - fn mul(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 * other.0) } -} - #[derive(Clone, Copy, Debug)] struct LineSegmentF32(pub ::Vf32);