Implement Euclid PathEvent <-> Segment adapters

This commit is contained in:
Patrick Walton 2019-01-11 11:03:37 -08:00
parent 3165a4a1b0
commit ddae909cbf
1 changed files with 223 additions and 21 deletions

View File

@ -233,7 +233,8 @@ impl Scene {
if let Some(ref fill) = path.fill {
let style = scene.push_paint(&Paint::from_svg_paint(&fill.paint));
let path = UsvgPathToPathEvents::new(path.segments.iter().cloned());
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
let path = SegmentsToPathEvents::new(path);
let path = PathTransformingIter::new(path, &transform);
let path = MonotonicConversionIter::new(path);
let outline = Outline::from_path_events(path);
@ -250,7 +251,8 @@ impl Scene {
let stroke_width = f32::max(stroke.width.value() as f32,
HAIRLINE_STROKE_WIDTH);
let path = UsvgPathToPathEvents::new(path.segments.iter().cloned());
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
let path = SegmentsToPathEvents::new(path);
let path = PathIter::new(path);
let path = StrokeToFillIter::new(path, StrokeStyle::new(stroke_width));
let path = PathTransformingIter::new(path, &transform);
@ -655,6 +657,7 @@ struct Segment {
baseline: LineSegmentF32,
ctrl: LineSegmentF32,
kind: SegmentKind,
flags: SegmentFlags,
}
impl Segment {
@ -663,14 +666,34 @@ impl Segment {
baseline: LineSegmentF32::default(),
ctrl: LineSegmentF32::default(),
kind: SegmentKind::None,
flags: SegmentFlags::empty(),
}
}
fn from_line(line: &LineSegmentF32) -> Segment {
fn line(line: &LineSegmentF32) -> Segment {
Segment {
baseline: *line,
ctrl: LineSegmentF32::default(),
kind: SegmentKind::Line,
flags: SegmentFlags::empty(),
}
}
fn quadratic(baseline: &LineSegmentF32, ctrl: &Point2DF32) -> Segment {
Segment {
baseline: *baseline,
ctrl: LineSegmentF32::new(ctrl, &Point2DF32::default()),
kind: SegmentKind::Cubic,
flags: SegmentFlags::empty(),
}
}
fn cubic(baseline: &LineSegmentF32, ctrl: &LineSegmentF32) -> Segment {
Segment {
baseline: *baseline,
ctrl: *ctrl,
kind: SegmentKind::Cubic,
flags: SegmentFlags::empty(),
}
}
@ -708,6 +731,7 @@ impl Segment {
baseline: self.baseline.reversed(),
ctrl: if self.is_quadratic_segment() { self.ctrl } else { self.ctrl.reversed() },
kind: self.kind,
flags: self.flags,
}
}
@ -723,6 +747,7 @@ impl Segment {
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
enum SegmentKind {
None,
Line,
@ -730,6 +755,13 @@ enum SegmentKind {
Cubic,
}
bitflags! {
struct SegmentFlags: u8 {
const FIRST_IN_SUBPATH = 0x01;
const CLOSES_SUBPATH = 0x02;
}
}
#[derive(Clone, Copy, Debug)]
struct CubicSegment<'s>(&'s Segment);
@ -799,14 +831,17 @@ impl<'s> CubicSegment<'s> {
let baseline1 = assemble(&p0123, &p0p3, 0, 1);
let ctrl1 = assemble(&p012p123, &p12p23, 1, 1);
// FIXME(pcwalton): Set flags appropriately!
return (Segment {
baseline: LineSegmentF32(baseline0),
ctrl: LineSegmentF32(ctrl0),
kind: SegmentKind::Cubic,
flags: SegmentFlags::empty(),
}, Segment {
baseline: LineSegmentF32(baseline1),
ctrl: LineSegmentF32(ctrl1),
kind: SegmentKind::Cubic,
flags: SegmentFlags::empty(),
})
}
@ -1700,33 +1735,200 @@ fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2DF32 {
transform.e as f32, transform.f as f32)
}
struct UsvgPathToPathEvents<I> where I: Iterator<Item = UsvgPathSegment> {
struct UsvgPathToSegments<I> where I: Iterator<Item = UsvgPathSegment> {
iter: I,
first_subpath_point: Point2DF32,
last_subpath_point: Point2DF32,
just_moved: bool,
}
impl<I> UsvgPathToPathEvents<I> where I: Iterator<Item = UsvgPathSegment> {
fn new(iter: I) -> UsvgPathToPathEvents<I> {
UsvgPathToPathEvents { iter }
impl<I> UsvgPathToSegments<I> where I: Iterator<Item = UsvgPathSegment> {
fn new(iter: I) -> UsvgPathToSegments<I> {
UsvgPathToSegments {
iter,
first_subpath_point: Point2DF32::default(),
last_subpath_point: Point2DF32::default(),
just_moved: false,
}
}
}
impl<I> Iterator for UsvgPathToPathEvents<I> where I: Iterator<Item = UsvgPathSegment> {
impl<I> Iterator for UsvgPathToSegments<I> where I: Iterator<Item = UsvgPathSegment> {
type Item = Segment;
fn next(&mut self) -> Option<Segment> {
match self.iter.next()? {
UsvgPathSegment::MoveTo { x, y } => {
let to = Point2DF32::new(x as f32, y as f32);
self.first_subpath_point = to;
self.last_subpath_point = to;
self.just_moved = true;
self.next()
}
UsvgPathSegment::LineTo { x, y } => {
let to = Point2DF32::new(x as f32, y as f32);
let mut segment =
Segment::line(&LineSegmentF32::new(&self.last_subpath_point, &to));
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
self.last_subpath_point = to;
self.just_moved = false;
Some(segment)
}
UsvgPathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
let ctrl0 = Point2DF32::new(x1 as f32, y1 as f32);
let ctrl1 = Point2DF32::new(x2 as f32, y2 as f32);
let to = Point2DF32::new(x as f32, y as f32);
let mut segment =
Segment::cubic(&LineSegmentF32::new(&self.last_subpath_point, &to),
&LineSegmentF32::new(&ctrl0, &ctrl1));
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
self.last_subpath_point = to;
self.just_moved = false;
Some(segment)
}
UsvgPathSegment::ClosePath => {
let mut segment = Segment::line(&LineSegmentF32::new(&self.last_subpath_point,
&self.first_subpath_point));
segment.flags.insert(SegmentFlags::CLOSES_SUBPATH);
self.just_moved = false;
self.last_subpath_point = self.first_subpath_point;
Some(segment)
}
}
}
}
// Euclid interoperability
//
// TODO(pcwalton): Remove this once we're fully on Pathfinder's native geometry.
struct PathEventsToSegments<I> where I: Iterator<Item = PathEvent> {
iter: I,
first_subpath_point: Point2DF32,
last_subpath_point: Point2DF32,
just_moved: bool,
}
impl<I> PathEventsToSegments<I> where I: Iterator<Item = PathEvent> {
fn new(iter: I) -> PathEventsToSegments<I> {
PathEventsToSegments {
iter,
first_subpath_point: Point2DF32::default(),
last_subpath_point: Point2DF32::default(),
just_moved: false,
}
}
}
impl<I> Iterator for PathEventsToSegments<I> where I: Iterator<Item = PathEvent> {
type Item = Segment;
fn next(&mut self) -> Option<Segment> {
match self.iter.next()? {
PathEvent::MoveTo(to) => {
let to = Point2DF32::from_euclid(to);
self.first_subpath_point = to;
self.last_subpath_point = to;
self.just_moved = true;
self.next()
}
PathEvent::LineTo(to) => {
let to = Point2DF32::from_euclid(to);
let mut segment =
Segment::line(&LineSegmentF32::new(&self.last_subpath_point, &to));
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
self.last_subpath_point = to;
self.just_moved = false;
Some(segment)
}
PathEvent::QuadraticTo(ctrl, to) => {
let (ctrl, to) = (Point2DF32::from_euclid(ctrl), Point2DF32::from_euclid(to));
let mut segment =
Segment::quadratic(&LineSegmentF32::new(&self.last_subpath_point, &to),
&ctrl);
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
self.last_subpath_point = to;
self.just_moved = false;
Some(segment)
}
PathEvent::CubicTo(ctrl0, ctrl1, to) => {
let ctrl0 = Point2DF32::from_euclid(ctrl0);
let ctrl1 = Point2DF32::from_euclid(ctrl1);
let to = Point2DF32::from_euclid(to);
let mut segment =
Segment::cubic(&LineSegmentF32::new(&self.last_subpath_point, &to),
&LineSegmentF32::new(&ctrl0, &ctrl1));
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
self.last_subpath_point = to;
self.just_moved = false;
Some(segment)
}
PathEvent::Close => {
let mut segment = Segment::line(&LineSegmentF32::new(&self.last_subpath_point,
&self.first_subpath_point));
segment.flags.insert(SegmentFlags::CLOSES_SUBPATH);
self.just_moved = false;
self.last_subpath_point = self.first_subpath_point;
Some(segment)
}
PathEvent::Arc(..) => panic!("TODO: arcs"),
}
}
}
struct SegmentsToPathEvents<I> where I: Iterator<Item = Segment> {
iter: I,
buffer: Option<PathEvent>,
}
impl<I> SegmentsToPathEvents<I> where I: Iterator<Item = Segment> {
fn new(iter: I) -> SegmentsToPathEvents<I> {
SegmentsToPathEvents { iter, buffer: None }
}
}
impl<I> Iterator for SegmentsToPathEvents<I> where I: Iterator<Item = Segment> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
match self.iter.next()? {
UsvgPathSegment::MoveTo { x, y } => {
Some(PathEvent::MoveTo(Point2D::new(x, y).to_f32()))
if let Some(event) = self.buffer.take() {
return Some(event);
}
UsvgPathSegment::LineTo { x, y } => {
Some(PathEvent::LineTo(Point2D::new(x, y).to_f32()))
let segment = self.iter.next()?;
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
return Some(PathEvent::Close);
}
UsvgPathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
Some(PathEvent::CubicTo(Point2D::new(x1, y1).to_f32(),
Point2D::new(x2, y2).to_f32(),
Point2D::new(x, y) .to_f32()))
let event = match segment.kind {
SegmentKind::None => return self.next(),
SegmentKind::Line => PathEvent::LineTo(segment.baseline.to().as_euclid()),
SegmentKind::Quadratic => {
PathEvent::QuadraticTo(segment.ctrl.from().as_euclid(),
segment.baseline.to().as_euclid())
}
UsvgPathSegment::ClosePath => Some(PathEvent::Close),
SegmentKind::Cubic => {
PathEvent::CubicTo(segment.ctrl.from().as_euclid(),
segment.ctrl.to().as_euclid(),
segment.baseline.to().as_euclid())
}
};
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
self.buffer = Some(event);
Some(PathEvent::MoveTo(segment.baseline.from().as_euclid()))
} else {
Some(event)
}
}
}
@ -1977,7 +2179,7 @@ impl ActiveEdge {
if segment.is_line_segment() {
let line_segment = segment.as_line_segment();
self.segment = match self.process_line_segment(&line_segment, built_object, tile_y) {
Some(lower_part) => Segment::from_line(&lower_part),
Some(lower_part) => Segment::line(&lower_part),
None => Segment::none(),
};
return;
@ -2005,7 +2207,7 @@ impl ActiveEdge {
self.segment = match self.process_line_segment(&line_segment,
built_object,
tile_y) {
Some(ref lower_part) => Segment::from_line(lower_part),
Some(ref lower_part) => Segment::line(lower_part),
None => Segment::none(),
};
return;