Build outlines from segments; move SIMD points into `geometry`

This commit is contained in:
Patrick Walton 2019-01-11 15:24:13 -08:00
parent c8bbb71dd4
commit 8ff3da8a68
7 changed files with 155 additions and 107 deletions

3
Cargo.lock generated
View File

@ -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]]

View File

@ -1,10 +1,14 @@
[package]
name = "pathfinder_geometry"
version = "0.3.0"
edition = "2018"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
[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"

View File

@ -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;

View File

@ -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 {

107
geometry/src/point.rs Normal file
View File

@ -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)
}
}

View File

@ -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,

View File

@ -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<I>(path_events: I) -> Outline where I: Iterator<Item = PathEvent> {
fn from_segments<I>(segments: I) -> Outline where I: Iterator<Item = Segment> {
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);
}
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 => {
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()));
}
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() {
outline.contours.push(current_contour)
}
@ -2179,64 +2172,6 @@ impl PartialOrd<ActiveEdge> for ActiveEdge {
// 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)]
struct LineSegmentF32(pub <Sse41 as Simd>::Vf32);