More bug fixes

This commit is contained in:
Patrick Walton 2019-01-01 01:12:18 -08:00
parent 6575012cc9
commit 68e0e791a4
2 changed files with 175 additions and 130 deletions

View File

@ -33,7 +33,8 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
void main() { void main() {
vec2 tileOrigin = computeTileOffset(aTileIndex, uFramebufferSize.x); vec2 tileOrigin = computeTileOffset(aTileIndex, uFramebufferSize.x);
vec2 from = aFrom, to = aTo; vec2 from = clamp(aFrom, vec2(0.0), uTileSize);
vec2 to = clamp(aTo, vec2(0.0), uTileSize);
vec2 position; vec2 position;
bool zeroArea = !(abs(from.x - to.x) > 0.1) || !(abs(uTileSize.y - min(from.y, to.y)) > 0.1); bool zeroArea = !(abs(from.x - to.x) > 0.1) || !(abs(uTileSize.y - min(from.y, to.y)) > 0.1);

View File

@ -22,6 +22,7 @@ use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
use jemallocator; use jemallocator;
use lyon_geom::cubic_bezier::Flattened; use lyon_geom::cubic_bezier::Flattened;
use lyon_geom::math::Transform;
use lyon_geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment}; use lyon_geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment};
use lyon_path::PathEvent; use lyon_path::PathEvent;
use lyon_path::iterator::PathIter; use lyon_path::iterator::PathIter;
@ -294,7 +295,9 @@ impl Scene {
} }
b"stroke-width" => { b"stroke-width" => {
if let Ok(width) = reader.decode(&attribute.value).parse() { if let Ok(width) = reader.decode(&attribute.value).parse() {
group_style.stroke_width = Some(width) let width: f32 = width;
//group_style.stroke_width = Some(1.0);
group_style.stroke_width = Some(width);
} }
} }
_ => {} _ => {}
@ -366,16 +369,18 @@ impl Scene {
} }
fn push_svg_path(&mut self, value: &str, style: StyleId, name: String) { fn push_svg_path(&mut self, value: &str, style: StyleId, name: String) {
if self.get_style(style).stroke_color.is_some() { let global_transform = Transform2D::create_scale(SCALE_FACTOR, SCALE_FACTOR);
let transform = global_transform.pre_mul(&self.get_style(style).transform);
if self.get_style(style).fill_color.is_some() {
let computed_style = self.get_style(style); let computed_style = self.get_style(style);
let mut path_parser = PathParser::from(&*value); let mut path_parser = PathParser::from(&*value);
let path = SvgPathToPathEvents::new(&mut path_parser); let path = SvgPathToPathEvents::new(&mut path_parser);
let path = PathIter::new(path); let path = PathTransformingIter::new(path, &transform);
let path = StrokeToFillIter::new(path, StrokeStyle::new(computed_style.stroke_width));
let path = MonotonicConversionIter::new(path); let path = MonotonicConversionIter::new(path);
let outline = Outline::from_path_events(path, computed_style); let outline = Outline::from_path_events(path, computed_style);
let color = match computed_style.stroke_color { let color = match computed_style.fill_color {
None => ColorU::black(), None => ColorU::black(),
Some(color) => ColorU::from_svg_color(color), Some(color) => ColorU::from_svg_color(color),
}; };
@ -384,14 +389,17 @@ impl Scene {
self.objects.push(PathObject::new(outline, color, style, name.clone())); self.objects.push(PathObject::new(outline, color, style, name.clone()));
} }
if self.get_style(style).fill_color.is_some() { if self.get_style(style).stroke_color.is_some() {
let computed_style = self.get_style(style); let computed_style = self.get_style(style);
let mut path_parser = PathParser::from(&*value); let mut path_parser = PathParser::from(&*value);
let path = SvgPathToPathEvents::new(&mut path_parser); let path = SvgPathToPathEvents::new(&mut path_parser);
let path = PathIter::new(path);
let path = StrokeToFillIter::new(path, StrokeStyle::new(computed_style.stroke_width));
let path = PathTransformingIter::new(path, &transform);
let path = MonotonicConversionIter::new(path); let path = MonotonicConversionIter::new(path);
let outline = Outline::from_path_events(path, computed_style); let outline = Outline::from_path_events(path, computed_style);
let color = match computed_style.fill_color { let color = match computed_style.stroke_color {
None => ColorU::black(), None => ColorU::black(),
Some(color) => ColorU::from_svg_color(color), Some(color) => ColorU::from_svg_color(color),
}; };
@ -436,55 +444,38 @@ impl Outline {
} }
} }
// NB: Assumes the path has already been transformed.
fn from_path_events<I>(path_events: I, style: &ComputedStyle) -> Outline fn from_path_events<I>(path_events: I, style: &ComputedStyle) -> Outline
where I: Iterator<Item = PathEvent> { 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;
let global_transform = Transform2D::create_scale(SCALE_FACTOR, SCALE_FACTOR);
let transform = global_transform.pre_mul(&style.transform);
for path_event in path_events { for path_event in path_events {
match path_event { match path_event {
PathEvent::MoveTo(to) => { PathEvent::MoveTo(to) => {
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_transformed_point(&to, current_contour.push_point(&to, PointFlags::empty(), &mut bounding_points);
PointFlags::empty(),
&transform,
&mut bounding_points);
} }
PathEvent::LineTo(to) => { PathEvent::LineTo(to) => {
current_contour.push_transformed_point(&to, current_contour.push_point(&to, PointFlags::empty(), &mut bounding_points);
PointFlags::empty(),
&transform,
&mut bounding_points);
} }
PathEvent::QuadraticTo(ctrl, to) => { PathEvent::QuadraticTo(ctrl, to) => {
current_contour.push_transformed_point(&ctrl, current_contour.push_point(&ctrl,
PointFlags::CONTROL_POINT_0, PointFlags::CONTROL_POINT_0,
&transform,
&mut bounding_points);
current_contour.push_transformed_point(&to,
PointFlags::empty(),
&transform,
&mut bounding_points); &mut bounding_points);
current_contour.push_point(&to, PointFlags::empty(), &mut bounding_points);
} }
PathEvent::CubicTo(ctrl0, ctrl1, to) => { PathEvent::CubicTo(ctrl0, ctrl1, to) => {
current_contour.push_transformed_point(&ctrl0, current_contour.push_point(&ctrl0,
PointFlags::CONTROL_POINT_0, PointFlags::CONTROL_POINT_0,
&transform,
&mut bounding_points); &mut bounding_points);
current_contour.push_transformed_point(&ctrl1, current_contour.push_point(&ctrl1,
PointFlags::CONTROL_POINT_1, PointFlags::CONTROL_POINT_1,
&transform,
&mut bounding_points);
current_contour.push_transformed_point(&to,
PointFlags::empty(),
&transform,
&mut bounding_points); &mut bounding_points);
current_contour.push_point(&to, PointFlags::empty(), &mut bounding_points);
} }
PathEvent::Close => { PathEvent::Close => {
if !current_contour.is_empty() { if !current_contour.is_empty() {
@ -527,21 +518,19 @@ impl Contour {
self.points[index as usize] self.points[index as usize]
} }
fn push_transformed_point(&mut self, fn push_point(&mut self,
point: &Point2D<f32>, point: &Point2D<f32>,
flags: PointFlags, flags: PointFlags,
transform: &Transform2D<f32>,
bounding_points: &mut Option<(Point2D<f32>, Point2D<f32>)>) { bounding_points: &mut Option<(Point2D<f32>, Point2D<f32>)>) {
let point = transform.transform_point(point); self.points.push(*point);
self.points.push(point);
self.flags.push(flags); self.flags.push(flags);
match *bounding_points { match *bounding_points {
Some((ref mut upper_left, ref mut lower_right)) => { Some((ref mut upper_left, ref mut lower_right)) => {
*upper_left = upper_left.min(point); *upper_left = upper_left.min(*point);
*lower_right = lower_right.max(point); *lower_right = lower_right.max(*point);
} }
None => *bounding_points = Some((point, point)), None => *bounding_points = Some((*point, *point)),
} }
} }
@ -636,19 +625,53 @@ impl Debug for Contour {
} }
for (index, segment) in self.iter().enumerate() { for (index, segment) in self.iter().enumerate() {
if index > 0 { if index > 0 {
formatter.write_str(",")?; formatter.write_str(" ")?;
} }
if formatter.alternate() { if formatter.alternate() {
formatter.write_str("\n ")?; formatter.write_str("\n ")?;
} else {
formatter.write_str(" ")?;
} }
segment.fmt(formatter)?; write_path_event(formatter, &segment)?;
} }
if formatter.alternate() { if formatter.alternate() {
formatter.write_str("\n")? formatter.write_str("\n")?
} }
formatter.write_str("]") formatter.write_str("]")?;
return Ok(());
fn write_path_event(formatter: &mut Formatter, path_event: &PathEvent) -> fmt::Result {
match *path_event {
PathEvent::Arc(..) => {
// TODO(pcwalton)
formatter.write_str("TODO: arcs")?;
}
PathEvent::Close => formatter.write_str("z")?,
PathEvent::MoveTo(ref to) => {
formatter.write_str("M")?;
write_point(formatter, to)?;
}
PathEvent::LineTo(ref to) => {
formatter.write_str("L")?;
write_point(formatter, to)?;
}
PathEvent::QuadraticTo(ref ctrl, ref to) => {
formatter.write_str("Q")?;
write_point(formatter, ctrl)?;
write_point(formatter, to)?;
}
PathEvent::CubicTo(ref ctrl0, ref ctrl1, ref to) => {
formatter.write_str("C")?;
write_point(formatter, ctrl0)?;
write_point(formatter, ctrl1)?;
write_point(formatter, to)?;
}
}
return Ok(());
}
fn write_point(formatter: &mut Formatter, point: &Point2D<f32>) -> fmt::Result {
write!(formatter, " {},{}", point.x, point.y)
}
} }
} }
@ -798,67 +821,8 @@ impl Segment {
const EPSILON: f32 = 0.0001; const EPSILON: f32 = 0.0001;
} }
fn clip_x(&self, range: Range<f32>) -> Option<Segment> {
// Trivial cases.
if (self.from.x <= range.start && self.to.x <= range.start) ||
(self.from.x >= range.end && self.to.x >= range.end) {
return None
}
let (start, end) = (f32::min(self.from.x, self.to.x), f32::max(self.from.x, self.to.x));
if start >= range.start && end <= range.end {
return Some(*self)
}
// FIXME(pcwalton): Reduce code duplication!
if let Some(mut line_segment) = self.as_line_segment() {
if let Some(t) = LineAxis::from_x(&line_segment).solve_for_t(range.start) {
let (prev, next) = line_segment.split(t);
if line_segment.from.x < line_segment.to.x {
line_segment = next
} else {
line_segment = prev
}
}
if let Some(t) = LineAxis::from_x(&line_segment).solve_for_t(range.end) {
let (prev, next) = line_segment.split(t);
if line_segment.from.x < line_segment.to.x {
line_segment = prev
} else {
line_segment = next
}
}
let clipped = Segment::from_line(&line_segment);
return Some(clipped);
}
// TODO(pcwalton): Don't degree elevate!
let mut cubic_segment = self.as_cubic_segment().unwrap();
if let Some(t) = CubicAxis::from_x(&cubic_segment).solve_for_t(range.start) {
let (prev, next) = cubic_segment.split(t);
if cubic_segment.from.x < cubic_segment.to.x {
cubic_segment = next
} else {
cubic_segment = prev
}
}
if let Some(t) = CubicAxis::from_x(&cubic_segment).solve_for_t(range.end) {
let (prev, next) = cubic_segment.split(t);
if cubic_segment.from.x < cubic_segment.to.x {
cubic_segment = prev
} else {
cubic_segment = next
}
}
let clipped = Segment::from_cubic(&cubic_segment);
return Some(clipped);
}
fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) { fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) {
// Trivial cases. // Trivial cases.
if self.from.y <= y && self.to.y <= y { if self.from.y <= y && self.to.y <= y {
return (Some(*self), None) return (Some(*self), None)
@ -877,9 +841,11 @@ impl Segment {
None => { None => {
// TODO(pcwalton): Don't degree elevate! // TODO(pcwalton): Don't degree elevate!
let cubic_segment = self.as_cubic_segment().unwrap(); let cubic_segment = self.as_cubic_segment().unwrap();
//println!("split_y({}): cubic_segment={:?}", y, cubic_segment);
let t = CubicAxis::from_y(&cubic_segment).solve_for_t(y); let t = CubicAxis::from_y(&cubic_segment).solve_for_t(y);
let t = t.expect("Failed to solve cubic for Y!"); let t = t.expect("Failed to solve cubic for Y!");
let (prev, next) = cubic_segment.split(t); let (prev, next) = cubic_segment.split(t);
//println!("... split at {} = {:?} / {:?}", t, prev, next);
(Segment::from_cubic(&prev), Segment::from_cubic(&next)) (Segment::from_cubic(&prev), Segment::from_cubic(&next))
} }
}; };
@ -900,6 +866,7 @@ impl Segment {
// TODO(pcwalton): Don't degree elevate! // TODO(pcwalton): Don't degree elevate!
let segment = self.as_cubic_segment().unwrap(); let segment = self.as_cubic_segment().unwrap();
//println!("generate_fill_primitives(segment={:?})", segment);
let flattener = Flattened::new(segment, FLATTENING_TOLERANCE); let flattener = Flattened::new(segment, FLATTENING_TOLERANCE);
let mut from = self.from; let mut from = self.from;
for to in flattener { for to in flattener {
@ -910,6 +877,14 @@ impl Segment {
fn generate_fill_primitives_for_line(mut segment: LineSegment<f32>, fn generate_fill_primitives_for_line(mut segment: LineSegment<f32>,
built_object: &mut BuiltObject, built_object: &mut BuiltObject,
tile_y: i16) { tile_y: i16) {
/*
println!("segment={:?} tile_y={} ({}-{})",
segment,
tile_y,
tile_y as f32 * TILE_HEIGHT,
(tile_y + 1) as f32 * TILE_HEIGHT);
*/
let winding = segment.from.x > segment.to.x; let winding = segment.from.x > segment.to.x;
let (segment_left, segment_right) = if !winding { let (segment_left, segment_right) = if !winding {
(segment.from.x, segment.to.x) (segment.from.x, segment.to.x)
@ -1454,13 +1429,25 @@ impl BuiltObject {
fn add_fill(&mut self, from: &Point2D<f32>, to: &Point2D<f32>, tile_x: i16, tile_y: i16) { fn add_fill(&mut self, from: &Point2D<f32>, to: &Point2D<f32>, tile_x: i16, tile_y: i16) {
let tile_origin = Vector2D::new(tile_x as f32 * TILE_WIDTH, tile_y as f32 * TILE_HEIGHT); let tile_origin = Vector2D::new(tile_x as f32 * TILE_WIDTH, tile_y as f32 * TILE_HEIGHT);
let tile_index = self.tile_coords_to_index(tile_x, tile_y); let tile_index = self.tile_coords_to_index(tile_x, tile_y);
self.fills.push(FillObjectPrimitive { let (from, to) = (*from - tile_origin, *to - tile_origin);
from: *from - tile_origin,
to: *to - tile_origin, /*
tile_x, println!("from={:?} to={:?}", from, to);
tile_y, debug_assert!(from.x > -EPSILON);
}); debug_assert!(from.x < TILE_WIDTH + EPSILON);
debug_assert!(to.x > -EPSILON);
debug_assert!(to.x < TILE_WIDTH + EPSILON);
debug_assert!(from.y > -EPSILON);
debug_assert!(from.y < TILE_HEIGHT + EPSILON);
debug_assert!(to.y > -EPSILON);
debug_assert!(to.y < TILE_HEIGHT + EPSILON);
*/
self.fills.push(FillObjectPrimitive { from, to, tile_x, tile_y });
self.solid_tiles.set(tile_index as usize, false); self.solid_tiles.set(tile_index as usize, false);
// FIXME(pcwalton): This is really sloppy!
const EPSILON: f32 = 0.25;
} }
// FIXME(pcwalton): Use a `Point2D<i16>` instead? // FIXME(pcwalton): Use a `Point2D<i16>` instead?
@ -1686,6 +1673,30 @@ impl<'a, I> Iterator for SvgPathToPathEvents<'a, I> where I: Iterator<Item = Svg
} }
} }
// Path transformation utilities
struct PathTransformingIter<I> where I: Iterator<Item = PathEvent> {
inner: I,
transform: Transform2D<f32>,
}
impl<I> Iterator for PathTransformingIter<I> where I: Iterator<Item = PathEvent> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
self.inner.next().map(|event| event.transform(&self.transform))
}
}
impl<I> PathTransformingIter<I> where I: Iterator<Item = PathEvent> {
fn new(inner: I, transform: &Transform2D<f32>) -> PathTransformingIter<I> {
PathTransformingIter {
inner,
transform: *transform,
}
}
}
// Monotonic conversion utilities // Monotonic conversion utilities
// TODO(pcwalton): I think we only need to be monotonic in Y, maybe? // TODO(pcwalton): I think we only need to be monotonic in Y, maybe?
@ -1722,22 +1733,24 @@ impl<I> Iterator for MonotonicConversionIter<I> where I: Iterator<Item = PathEve
ctrl2: ctrl1, ctrl2: ctrl1,
to, to,
}; };
//println!("considering segment {:?}...", segment);
if segment.is_monotonic() { if segment.is_monotonic() {
//println!("... is monotonic");
self.last_point = to; self.last_point = to;
return Some(PathEvent::CubicTo(ctrl0, ctrl1, to)) return Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
} }
// FIXME(pcwalton): O(n^2)! // FIXME(pcwalton): O(n^2)!
let mut t = None; let mut t = 1.0;
segment.for_each_monotonic_t(|split_t| { segment.for_each_monotonic_t(|split_t| {
if t.is_none() { //println!("... split t={}", split_t);
t = Some(split_t) t = f32::min(t, split_t);
}
}); });
let t = t.unwrap();
if t_is_too_close_to_zero_or_one(t) { if t_is_too_close_to_zero_or_one(t) {
//println!("... segment t={} is too close to bounds, pushing", t);
self.last_point = to; self.last_point = to;
return Some(PathEvent::CubicTo(ctrl0, ctrl1, to)) return Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
} }
//println!("... making segment monotonic @ t={}", t);
let (prev, next) = segment.split(t); let (prev, next) = segment.split(t);
self.last_point = next.from; self.last_point = next.from;
self.buffer = Some(PathEvent::CubicTo(next.ctrl1, next.ctrl2, next.to)); self.buffer = Some(PathEvent::CubicTo(next.ctrl1, next.ctrl2, next.to));
@ -1750,13 +1763,11 @@ impl<I> Iterator for MonotonicConversionIter<I> where I: Iterator<Item = PathEve
return Some(PathEvent::QuadraticTo(ctrl, to)) return Some(PathEvent::QuadraticTo(ctrl, to))
} }
// FIXME(pcwalton): O(n^2)! // FIXME(pcwalton): O(n^2)!
let mut t = None; let mut t = 1.0;
segment.for_each_monotonic_t(|split_t| { segment.for_each_monotonic_t(|split_t| {
if t.is_none() { //println!("... split t={}", split_t);
t = Some(split_t) t = f32::min(t, split_t);
}
}); });
let t = t.unwrap();
if t_is_too_close_to_zero_or_one(t) { if t_is_too_close_to_zero_or_one(t) {
self.last_point = to; self.last_point = to;
return Some(PathEvent::QuadraticTo(ctrl, to)) return Some(PathEvent::QuadraticTo(ctrl, to))
@ -1976,6 +1987,39 @@ impl PartialOrd<ActiveEdge> for ActiveEdge {
} }
} }
// Path utilities
/*
fn cubic_segment_is_nearly_monotonic(segment: &CubicBezierSegment<f32>) -> bool {
let min_x = f32::min(segment.from.x, segment.to.x) - EPSILON;
let max_x = f32::max(segment.from.x, segment.to.x) + EPSILON;
let min_y = f32::min(segment.from.y, segment.to.y) - EPSILON;
let max_y = f32::max(segment.from.y, segment.to.y) + EPSILON;
return min_x <= segment.ctrl1.x && segment.ctrl1.x <= max_x &&
min_x <= segment.ctrl2.x && segment.ctrl2.x <= max_x &&
min_y <= segment.ctrl1.y && segment.ctrl1.y <= max_y &&
min_y <= segment.ctrl2.y && segment.ctrl2.y <= max_y;
const EPSILON: f32 = 0.1;
}
*/
fn cubic_segment_is_nearly_monotonic(segment: &CubicBezierSegment<f32>) -> bool {
let mut t = None;
segment.for_each_monotonic_t(|split_t| {
if t.is_none() {
t = Some(split_t)
}
});
return match t {
None => true,
Some(t) => t < EPSILON || t > 1.0 - EPSILON,
};
const EPSILON: f32 = 0.01;
}
// Trivial utilities // Trivial utilities
fn lerp(a: f32, b: f32, t: f32) -> f32 { fn lerp(a: f32, b: f32, t: f32) -> f32 {