This commit is contained in:
Patrick Walton 2018-12-13 16:22:06 -08:00
parent 670a6001fb
commit cfd1965a42
1 changed files with 135 additions and 98 deletions

View File

@ -443,28 +443,31 @@ impl Contour {
fn segment_after(&self, point_index: usize) -> Segment { fn segment_after(&self, point_index: usize) -> Segment {
debug_assert!(self.point_is_endpoint(point_index)); debug_assert!(self.point_is_endpoint(point_index));
let mut segment = Segment::new();
segment.from = self.points[point_index];
segment.flags |= SegmentFlags::HAS_ENDPOINTS;
let point1_index = self.add_to_point_index(point_index, 1); let point1_index = self.add_to_point_index(point_index, 1);
if self.point_is_endpoint(point1_index) { if self.point_is_endpoint(point1_index) {
return Segment::Line(LineSegment { segment.to = self.points[point1_index];
from: self.points[point_index], } else {
to: self.points[point1_index], segment.ctrl0 = self.points[point1_index];
}) segment.flags |= SegmentFlags::HAS_CONTROL_POINT_0;
let point2_index = self.add_to_point_index(point_index, 2);
if self.point_is_endpoint(point2_index) {
segment.to = self.points[point2_index];
} else {
segment.ctrl1 = self.points[point2_index];
segment.flags |= SegmentFlags::HAS_CONTROL_POINT_1;
let point3_index = self.add_to_point_index(point_index, 3);
segment.to = self.points[point3_index];
}
} }
let point2_index = self.add_to_point_index(point_index, 2);
if self.point_is_endpoint(point2_index) { segment
return Segment::Quadratic(QuadraticBezierSegment {
from: self.points[point_index],
ctrl: self.points[point1_index],
to: self.points[point2_index],
})
}
let point3_index = self.add_to_point_index(point_index, 3);
Segment::Cubic(CubicBezierSegment {
from: self.points[point_index],
ctrl1: self.points[point1_index],
ctrl2: self.points[point2_index],
to: self.points[point3_index],
})
} }
fn point_is_endpoint(&self, point_index: usize) -> bool { fn point_is_endpoint(&self, point_index: usize) -> bool {
@ -473,7 +476,12 @@ impl Contour {
} }
fn add_to_point_index(&self, point_index: usize, addend: usize) -> usize { fn add_to_point_index(&self, point_index: usize, addend: usize) -> usize {
(point_index + addend) % self.points.len() let (index, limit) = (point_index + addend, self.points.len());
if index >= limit {
index - limit
} else {
index
}
} }
} }
@ -484,80 +492,109 @@ struct PointIndex {
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum Segment { struct Segment {
None, from: Point2D<f32>,
Line(LineSegment<f32>), ctrl0: Point2D<f32>,
Quadratic(QuadraticBezierSegment<f32>), ctrl1: Point2D<f32>,
Cubic(CubicBezierSegment<f32>), to: Point2D<f32>,
flags: SegmentFlags,
} }
impl Segment { impl Segment {
fn is_none(&self) -> bool { fn new() -> Segment {
match *self { Segment {
Segment::None => true, from: Point2D::zero(),
_ => false, ctrl0: Point2D::zero(),
ctrl1: Point2D::zero(),
to: Point2D::zero(),
flags: SegmentFlags::empty(),
} }
} }
fn endpoints(&self) -> (Point2D<f32>, Point2D<f32>) { fn from_line(line: &LineSegment<f32>) -> Segment {
match *self { Segment {
Segment::Line(ref line) => (line.from, line.to), from: line.from,
Segment::Quadratic(ref curve) => (curve.from, curve.to), ctrl0: Point2D::zero(),
Segment::Cubic(ref curve) => (curve.from, curve.to), ctrl1: Point2D::zero(),
Segment::None => unreachable!(), to: line.to,
flags: SegmentFlags::HAS_ENDPOINTS,
} }
} }
fn from_quadratic(curve: &QuadraticBezierSegment<f32>) -> Segment {
Segment {
from: curve.from,
ctrl0: curve.ctrl,
ctrl1: Point2D::zero(),
to: curve.to,
flags: SegmentFlags::HAS_ENDPOINTS | SegmentFlags::HAS_CONTROL_POINT_0
}
}
fn from_cubic(curve: &CubicBezierSegment<f32>) -> Segment {
Segment {
from: curve.from,
ctrl0: curve.ctrl1,
ctrl1: curve.ctrl2,
to: curve.to,
flags: SegmentFlags::HAS_ENDPOINTS | SegmentFlags::HAS_CONTROL_POINT_0 |
SegmentFlags::HAS_CONTROL_POINT_1,
}
}
fn is_none(&self) -> bool {
!self.flags.contains(SegmentFlags::HAS_ENDPOINTS)
}
// Note: If we convert these to monotonic then we can optimize this method. // Note: If we convert these to monotonic then we can optimize this method.
// TODO(pcwalton): Consider changing the representation of `Segment` to remove the code // TODO(pcwalton): Consider changing the representation of `Segment` to remove the code
// duplication in the branches here? // duplication in the branches here?
fn min_y(&self) -> f32 { fn min_y(&self) -> f32 {
match *self { let mut min_y = f32::min(self.from.y, self.to.y);
Segment::Line(ref line) => f32::min(line.from.y, line.to.y), if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
Segment::Quadratic(ref curve) => { min_y = f32::min(min_y, self.ctrl0.y)
f32::min(f32::min(curve.from.y, curve.ctrl.y), curve.to.y)
}
Segment::Cubic(ref curve) => {
f32::min(f32::min(f32::min(curve.from.y, curve.ctrl1.y), curve.ctrl2.y),
curve.to.y)
}
Segment::None => unreachable!(),
} }
if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
min_y = f32::min(min_y, self.ctrl1.y)
}
min_y
} }
fn clip_y(&self, y: f32) -> ClippedSegments { fn clip_y(&self, y: f32) -> ClippedSegments {
let (from, to) = self.endpoints(); if self.from.y < y && self.to.y < y {
if from.y < y && to.y < y {
return ClippedSegments { min: Some(*self), max: None } return ClippedSegments { min: Some(*self), max: None }
} }
if from.y > y && to.y > y { if self.from.y > y && self.to.y > y {
return ClippedSegments { min: None, max: Some(*self) } return ClippedSegments { min: None, max: Some(*self) }
} }
let (prev, next) = match *self { let (prev, next) = if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
Segment::Line(ref line) => { let curve = CubicBezierSegment {
let (prev, next) = line.split(line.solve_t_for_y(y)); from: self.from,
(Segment::Line(prev), Segment::Line(next)) ctrl1: self.ctrl0,
} ctrl2: self.ctrl1,
Segment::Quadratic(ref curve) => { to: self.to,
let (prev, next) = curve.split(curve.assume_monotonic().solve_t_for_y(y)); };
(Segment::Quadratic(prev), Segment::Quadratic(next)) let swapped_curve = CubicBezierSegment {
} from: curve.from.yx(),
Segment::Cubic(ref curve) => { ctrl1: curve.ctrl1.yx(),
let swapped_curve = CubicBezierSegment { ctrl2: curve.ctrl2.yx(),
from: curve.from.yx(), to: curve.to.yx(),
ctrl1: curve.ctrl1.yx(), };
ctrl2: curve.ctrl2.yx(), let (prev, next) = curve.split(
to: curve.to.yx(), swapped_curve.assume_monotonic().solve_t_for_x(y, 0.0..1.0, TOLERANCE));
}; (Segment::from_cubic(&prev), Segment::from_cubic(&next))
let (prev, next) = curve.split( } else if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
swapped_curve.assume_monotonic().solve_t_for_x(y, 0.0..1.0, TOLERANCE)); let curve = QuadraticBezierSegment { from: self.from, ctrl: self.ctrl0, to: self.to };
(Segment::Cubic(prev), Segment::Cubic(next)) let (prev, next) = curve.split(curve.assume_monotonic().solve_t_for_y(y));
} (Segment::from_quadratic(&prev), Segment::from_quadratic(&next))
Segment::None => unreachable!(), } else {
let line = LineSegment { from: self.from, to: self.to };
let (prev, next) = line.split(line.solve_t_for_y(y));
(Segment::from_line(&prev), Segment::from_line(&next))
}; };
if from.y <= to.y { if self.from.y <= self.to.y {
return ClippedSegments { min: Some(prev), max: Some(next) }; return ClippedSegments { min: Some(prev), max: Some(next) };
} else { } else {
return ClippedSegments { min: Some(next), max: Some(prev) }; return ClippedSegments { min: Some(next), max: Some(prev) };
@ -567,30 +604,23 @@ impl Segment {
} }
fn translate(&self, by: &Vector2D<f32>) -> Segment { fn translate(&self, by: &Vector2D<f32>) -> Segment {
match *self { let flags = self.flags;
Segment::Line(ref line) => { let (from, to) = if flags.contains(SegmentFlags::HAS_ENDPOINTS) {
Segment::Line(LineSegment { (self.from + *by, self.to + *by)
from: line.from + *by, } else {
to: line.to + *by, (Point2D::zero(), Point2D::zero())
}) };
} let ctrl0 = if flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
Segment::Quadratic(ref curve) => { self.ctrl0 + *by
Segment::Quadratic(QuadraticBezierSegment { } else {
from: curve.from + *by, Point2D::zero()
ctrl: curve.ctrl + *by, };
to: curve.to + *by, let ctrl1 = if flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
}) self.ctrl1 + *by
} } else {
Segment::Cubic(ref curve) => { Point2D::zero()
Segment::Cubic(CubicBezierSegment { };
from: curve.from + *by, Segment { from, ctrl0, ctrl1, to, flags }
ctrl1: curve.ctrl1 + *by,
ctrl2: curve.ctrl2 + *by,
to: curve.to + *by,
})
}
Segment::None => unreachable!(),
}
} }
} }
@ -599,6 +629,14 @@ struct ClippedSegments {
max: Option<Segment>, max: Option<Segment>,
} }
bitflags! {
struct SegmentFlags: u8 {
const HAS_ENDPOINTS = 0x01;
const HAS_CONTROL_POINT_0 = 0x02;
const HAS_CONTROL_POINT_1 = 0x04;
}
}
// Tiling // Tiling
const TILE_WIDTH: f32 = 4.0; const TILE_WIDTH: f32 = 4.0;
@ -690,9 +728,8 @@ fn process_active_edge(active_edge: &mut Segment,
strip.push_segment(upper_segment); strip.push_segment(upper_segment);
// FIXME(pcwalton): Assumes x-monotonicity! // FIXME(pcwalton): Assumes x-monotonicity!
// FIXME(pcwalton): The min call below is a hack! // FIXME(pcwalton): The min call below is a hack!
let (from, to) = upper_segment.endpoints(); let from_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.from.x));
let from_x = f32::max(0.0, f32::min(active_intervals.extent(), from.x)); let to_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.to.x));
let to_x = f32::max(0.0, f32::min(active_intervals.extent(), to.x));
if from_x < to_x { if from_x < to_x {
active_intervals.add(IntervalRange::new(from_x, to_x, -1.0)) active_intervals.add(IntervalRange::new(from_x, to_x, -1.0))
} else { } else {
@ -702,7 +739,7 @@ fn process_active_edge(active_edge: &mut Segment,
match clipped.max { match clipped.max {
Some(lower_segment) => *active_edge = lower_segment, Some(lower_segment) => *active_edge = lower_segment,
None => *active_edge = Segment::None, None => *active_edge = Segment::new(),
} }
} }