Build outlines from segments; move SIMD points into `geometry`
This commit is contained in:
parent
c8bbb71dd4
commit
8ff3da8a68
|
@ -429,9 +429,12 @@ name = "pathfinder_geometry"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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 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)",
|
"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]]
|
[[package]]
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pathfinder_geometry"
|
name = "pathfinder_geometry"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
edition = "2018"
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arrayvec = "0.4"
|
arrayvec = "0.4"
|
||||||
|
euclid = "0.19"
|
||||||
|
lyon_geom = "0.12"
|
||||||
lyon_path = "0.12"
|
lyon_path = "0.12"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
simdeez = "0.4"
|
||||||
|
|
|
@ -12,16 +12,16 @@
|
||||||
//!
|
//!
|
||||||
//! These may be merged into upstream Lyon eventually.
|
//! These may be merged into upstream Lyon eventually.
|
||||||
|
|
||||||
extern crate arrayvec;
|
use simdeez::sse41::Sse41;
|
||||||
extern crate lyon_path;
|
|
||||||
|
|
||||||
use lyon_path::geom as lyon_geom;
|
// TODO(pcwalton): Make this configurable.
|
||||||
use lyon_path::geom::euclid;
|
pub type SimdImpl = Sse41;
|
||||||
|
|
||||||
pub mod clip;
|
pub mod clip;
|
||||||
pub mod cubic_to_quadratic;
|
pub mod cubic_to_quadratic;
|
||||||
pub mod normals;
|
pub mod normals;
|
||||||
pub mod orientation;
|
pub mod orientation;
|
||||||
|
pub mod point;
|
||||||
pub mod segments;
|
pub mod segments;
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use crate::orientation::Orientation;
|
||||||
use euclid::approxeq::ApproxEq;
|
use euclid::approxeq::ApproxEq;
|
||||||
use euclid::{Point2D, Vector2D};
|
use euclid::{Point2D, Vector2D};
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
use orientation::Orientation;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SegmentNormals {
|
pub struct SegmentNormals {
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
// pathfinder/geometry/src/point.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <SimdImpl as Simd>::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<f32>) -> Point2DF32 {
|
||||||
|
Point2DF32::new(point.x, point.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn as_euclid(&self) -> Point2D<f32> {
|
||||||
|
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<Point2DF32> for Point2DF32 {
|
||||||
|
type Output = Point2DF32;
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: Point2DF32) -> Point2DF32 {
|
||||||
|
Point2DF32(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Point2DF32> for Point2DF32 {
|
||||||
|
type Output = Point2DF32;
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, other: Point2DF32) -> Point2DF32 {
|
||||||
|
Point2DF32(self.0 - other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Point2DF32> for Point2DF32 {
|
||||||
|
type Output = Point2DF32;
|
||||||
|
#[inline]
|
||||||
|
fn mul(self, other: Point2DF32) -> Point2DF32 {
|
||||||
|
Point2DF32(self.0 * other.0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,11 +10,10 @@
|
||||||
|
|
||||||
//! Utilities for converting path strokes to fills.
|
//! Utilities for converting path strokes to fills.
|
||||||
|
|
||||||
|
use crate::segments::{Segment, SegmentIter};
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
use lyon_path::iterator::PathIterator;
|
use lyon_path::iterator::PathIterator;
|
||||||
|
|
||||||
use segments::{Segment, SegmentIter};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct StrokeStyle {
|
pub struct StrokeStyle {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
|
|
|
@ -27,6 +27,7 @@ use hashbrown::HashMap;
|
||||||
use jemallocator;
|
use jemallocator;
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
use lyon_path::iterator::PathIter;
|
use lyon_path::iterator::PathIter;
|
||||||
|
use pathfinder_geometry::point::Point2DF32;
|
||||||
use pathfinder_geometry::stroke::{StrokeStyle, StrokeToFillIter};
|
use pathfinder_geometry::stroke::{StrokeStyle, StrokeToFillIter};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||||
|
@ -40,7 +41,7 @@ use std::fs::File;
|
||||||
use std::io::{self, BufWriter, Write};
|
use std::io::{self, BufWriter, Write};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Add, Mul, Sub};
|
use std::ops::Sub;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
@ -236,8 +237,7 @@ impl Scene {
|
||||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
||||||
let path = PathTransformingIter::new(path, &transform);
|
let path = PathTransformingIter::new(path, &transform);
|
||||||
let path = MonotonicConversionIter::new(path);
|
let path = MonotonicConversionIter::new(path);
|
||||||
let path = SegmentsToPathEvents::new(path);
|
let outline = Outline::from_segments(path);
|
||||||
let outline = Outline::from_path_events(path);
|
|
||||||
|
|
||||||
scene.bounds = scene.bounds.union(&outline.bounds);
|
scene.bounds = scene.bounds.union(&outline.bounds);
|
||||||
scene.objects.push(PathObject::new(outline,
|
scene.objects.push(PathObject::new(outline,
|
||||||
|
@ -258,8 +258,7 @@ impl Scene {
|
||||||
let path = PathEventsToSegments::new(path);
|
let path = PathEventsToSegments::new(path);
|
||||||
let path = PathTransformingIter::new(path, &transform);
|
let path = PathTransformingIter::new(path, &transform);
|
||||||
let path = MonotonicConversionIter::new(path);
|
let path = MonotonicConversionIter::new(path);
|
||||||
let path = SegmentsToPathEvents::new(path);
|
let outline = Outline::from_segments(path);
|
||||||
let outline = Outline::from_path_events(path);
|
|
||||||
|
|
||||||
scene.bounds = scene.bounds.union(&outline.bounds);
|
scene.bounds = scene.bounds.union(&outline.bounds);
|
||||||
scene.objects.push(PathObject::new(outline,
|
scene.objects.push(PathObject::new(outline,
|
||||||
|
@ -350,54 +349,48 @@ impl Outline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Assumes the path has already been transformed.
|
fn from_segments<I>(segments: I) -> Outline where I: Iterator<Item = Segment> {
|
||||||
fn from_path_events<I>(path_events: I) -> Outline where I: Iterator<Item = PathEvent> {
|
|
||||||
let mut outline = Outline::new();
|
let mut outline = Outline::new();
|
||||||
let mut current_contour = Contour::new();
|
let mut current_contour = Contour::new();
|
||||||
let mut bounding_points = None;
|
let mut bounding_points = None;
|
||||||
|
|
||||||
for path_event in path_events {
|
for segment in segments {
|
||||||
match path_event {
|
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
PathEvent::LineTo(to) => {
|
|
||||||
current_contour.push_point(Point2DF32::from_euclid(to),
|
|
||||||
PointFlags::empty(),
|
|
||||||
&mut bounding_points);
|
|
||||||
}
|
|
||||||
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),
|
|
||||||
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() {
|
if !current_contour.is_empty() {
|
||||||
outline.contours.push(mem::replace(&mut current_contour, Contour::new()));
|
outline.contours.push(mem::replace(&mut current_contour, Contour::new()));
|
||||||
}
|
}
|
||||||
|
current_contour.push_point(segment.baseline.from(),
|
||||||
|
PointFlags::empty(),
|
||||||
|
&mut bounding_points);
|
||||||
}
|
}
|
||||||
PathEvent::Arc(..) => unimplemented!("arcs"),
|
|
||||||
|
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
||||||
|
if !current_contour.is_empty() {
|
||||||
|
outline.contours.push(mem::replace(&mut current_contour, Contour::new()));
|
||||||
|
}
|
||||||
|
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(segment.baseline.to(),
|
||||||
|
PointFlags::empty(),
|
||||||
|
&mut bounding_points);
|
||||||
|
}
|
||||||
|
|
||||||
if !current_contour.is_empty() {
|
if !current_contour.is_empty() {
|
||||||
outline.contours.push(current_contour)
|
outline.contours.push(current_contour)
|
||||||
}
|
}
|
||||||
|
@ -2179,64 +2172,6 @@ impl PartialOrd<ActiveEdge> for ActiveEdge {
|
||||||
|
|
||||||
// Geometry
|
// Geometry
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
struct Point2DF32(<Sse41 as Simd>::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<f32>) -> Point2DF32 { Point2DF32::new(point.x, point.y) }
|
|
||||||
fn as_euclid(&self) -> Point2D<f32> { 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: <Sse41 as Simd>::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<Point2DF32> for Point2DF32 {
|
|
||||||
type Output = Point2DF32;
|
|
||||||
fn add(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 + other.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Point2DF32> for Point2DF32 {
|
|
||||||
type Output = Point2DF32;
|
|
||||||
fn sub(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 - other.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Point2DF32> for Point2DF32 {
|
|
||||||
type Output = Point2DF32;
|
|
||||||
fn mul(self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0 * other.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct LineSegmentF32(pub <Sse41 as Simd>::Vf32);
|
struct LineSegmentF32(pub <Sse41 as Simd>::Vf32);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue