diff --git a/Cargo.lock b/Cargo.lock index 1215fb9f..5db1c9d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,14 +358,6 @@ dependencies = [ "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "euclid" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "euclid" version = "0.19.0" @@ -781,24 +773,6 @@ dependencies = [ "linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lyon_algorithms" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lyon_path 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lyon_geom" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lyon_geom" version = "0.12.1" @@ -809,14 +783,6 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lyon_path" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lyon_geom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lyon_path" version = "0.12.0" @@ -926,14 +892,6 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "num-traits" version = "0.2.6" @@ -1014,7 +972,7 @@ name = "pathfinder_path_utils" version = "0.2.0" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "lyon_path 0.11.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.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1518,10 +1476,10 @@ name = "tile-svg" version = "0.1.0" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lyon_algorithms 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lyon_path 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lyon_geom 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_path_utils 0.2.0", "quick-xml 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1744,7 +1702,6 @@ source = "git+https://github.com/SergioBenitez/ring?branch=v0.12#9ccfa153a27aecc "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -"checksum euclid 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59b34ec7d95d70d5cda27301d6182bc17abce8b5b52e260f5ff32c677923bbb0" "checksum euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f" "checksum expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c470ccb972f2088549b023db8029ed9da9426f5affbf9b62efff7009ab8ed5b1" "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" @@ -1789,10 +1746,7 @@ source = "git+https://github.com/SergioBenitez/ring?branch=v0.12#9ccfa153a27aecc "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" "checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" -"checksum lyon_algorithms 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3ebe7b9bffa94e7d8d332992bd1d8b9c0318170862487f0a2d7e04bcc5aabbe" -"checksum lyon_geom 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ee0dc4aec93a8fd9109362bebfbad0ace69b8629937f954ecc8eea1de63146" "checksum lyon_geom 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d69cc8d0b54ed6d49ed2ef6b465e67ee89e92dfcb4bd839cbd58dc189c14efe8" -"checksum lyon_path 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98c39b845796d4590197e2b2b97202e31b69071116a541bfddb52f50680318f0" "checksum lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9dc8e0746b7cca11960b602f7fe037bb067746a01eab4aa502fed1494544843" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" @@ -1807,7 +1761,6 @@ source = "git+https://github.com/SergioBenitez/ring?branch=v0.12#9ccfa153a27aecc "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9833ab0efe5361b1e2122a0544a5d3359576911a42cb098c2e59be8650807367" diff --git a/path-utils/Cargo.toml b/path-utils/Cargo.toml index 961bc190..097741ca 100644 --- a/path-utils/Cargo.toml +++ b/path-utils/Cargo.toml @@ -5,6 +5,6 @@ authors = ["Patrick Walton "] [dependencies] arrayvec = "0.4" -lyon_path = "0.11" +lyon_path = "0.12" serde = "1.0" serde_derive = "1.0" diff --git a/utils/tile-svg/Cargo.toml b/utils/tile-svg/Cargo.toml index a672bd3c..437a9020 100644 --- a/utils/tile-svg/Cargo.toml +++ b/utils/tile-svg/Cargo.toml @@ -6,10 +6,10 @@ edition = "2018" [dependencies] bitflags = "1.0" -euclid = "0.18" +euclid = "0.19" jemallocator = "0.1" -lyon_algorithms = "0.11" -lyon_path = "0.11" +lyon_geom = "0.12" +lyon_path = "0.12" quick-xml = "0.12" svgtypes = "0.2" diff --git a/utils/tile-svg/src/main.rs b/utils/tile-svg/src/main.rs index bb1179c5..af777bb6 100644 --- a/utils/tile-svg/src/main.rs +++ b/utils/tile-svg/src/main.rs @@ -16,9 +16,9 @@ extern crate quickcheck; #[cfg(test)] extern crate rand; -use euclid::{Point2D, Rect, Transform2D, Vector2D}; +use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use jemallocator; -use lyon_algorithms::geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment}; +use lyon_geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment}; use lyon_path::PathEvent; use lyon_path::iterator::PathIter; use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter}; @@ -26,6 +26,7 @@ use quick_xml::Reader; use quick_xml::events::Event; use std::cmp::Ordering; use std::env; +use std::fmt::{self, Debug, Formatter}; use std::mem; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -36,6 +37,9 @@ use svgtypes::{TransformListToken}; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; +// TODO(pcwalton): Make this configurable. +const SCALE_FACTOR: f32 = 8.0; + #[derive(Default)] struct GroupStyle { fill_color: Option, @@ -66,29 +70,34 @@ impl ComputedStyle { fn main() { let path = PathBuf::from(env::args().skip(1).next().unwrap()); let scene = Scene::from_path(&path); + println!("bounds: {:?}", scene.bounds); - const RUNS: u32 = 1000; + const RUNS: u32 = 100; let start_time = Instant::now(); + let mut primitives = vec![]; for _ in 0..RUNS { - scene.generate_tiles(); + primitives = scene.generate_tiles(); } let elapsed_time = Instant::now() - start_time; let elapsed_ms = elapsed_time.as_secs() as f64 * 1000.0 + elapsed_time.subsec_micros() as f64 / 1000.0; println!("{}ms elapsed", elapsed_ms / RUNS as f64); + println!("{} primitives generated", primitives.len()); } #[derive(Debug)] struct Scene { objects: Vec, styles: Vec, - //bounds: Rect, + bounds: Rect, + view_box: Option>, } #[derive(Debug)] struct PathObject { outline: Outline, style: StyleId, + name: String, } #[derive(Clone, Copy, PartialEq, Debug)] @@ -96,15 +105,14 @@ struct StyleId(u32); impl Scene { fn new() -> Scene { - Scene { - objects: vec![], - styles: vec![], - } + Scene { objects: vec![], styles: vec![], bounds: Rect::zero(), view_box: None } } fn from_path(path: &Path) -> Scene { let mut reader = Reader::from_file(&path).unwrap(); + let global_transform = Transform2D::create_scale(SCALE_FACTOR, SCALE_FACTOR); + let mut xml_buffer = vec![]; let mut group_styles = vec![]; let mut style = None; @@ -116,16 +124,19 @@ impl Scene { Ok(Event::Start(ref event)) | Ok(Event::Empty(ref event)) if event.name() == b"path" => { let attributes = event.attributes(); + let (mut encoded_path, mut name) = (String::new(), String::new()); for attribute in attributes { let attribute = attribute.unwrap(); - if attribute.key != b"d" { - continue + if attribute.key == b"d" { + encoded_path = reader.decode(&attribute.value).to_string(); + } else if attribute.key == b"id" { + name = reader.decode(&attribute.value).to_string(); } - let value = reader.decode(&attribute.value); - let style = scene.ensure_style(&mut style, &mut group_styles); - scene.push_svg_path(&*value, style); } + let style = scene.ensure_style(&mut style, &mut group_styles); + scene.push_svg_path(&encoded_path, style, name); } + Ok(Event::Start(ref event)) if event.name() == b"g" => { let mut group_style = GroupStyle::default(); let attributes = event.attributes(); @@ -168,9 +179,28 @@ impl Scene { _ => {} } } + group_styles.push(group_style); style = None; } + + Ok(Event::Start(ref event)) if event.name() == b"svg" => { + let attributes = event.attributes(); + for attribute in attributes { + let attribute = attribute.unwrap(); + if attribute.key == b"viewBox" { + let view_box = reader.decode(&attribute.value); + let mut elements = view_box.split_whitespace() + .map(|value| f32::from_str(value).unwrap()); + let view_box = Rect::new(Point2D::new(elements.next().unwrap(), + elements.next().unwrap()), + Size2D::new(elements.next().unwrap(), + elements.next().unwrap())); + scene.view_box = Some(global_transform.transform_rect(&view_box)); + } + } + } + Ok(Event::Eof) | Err(_) => break, Ok(_) => {} } @@ -212,16 +242,18 @@ impl Scene { &self.styles[style.0 as usize] } - fn generate_tiles(&self) { - let mut strips = vec![]; - for object in &self.objects { - let mut tiler = Tiler::from_outline(&object.outline); - tiler.generate_tiles(&mut strips); + fn generate_tiles(&self) -> Vec { + let mut primitives = vec![]; + for (index, object) in self.objects.iter().enumerate() { + //println!("{} ({}): {:?}", index, object.name, object.outline.bounds); + let mut tiler = Tiler::from_outline(&object.outline, &self.view_box, &mut primitives); + tiler.generate_tiles(); // TODO(pcwalton) } + primitives } - fn push_svg_path(&mut self, value: &str, style: StyleId) { + fn push_svg_path(&mut self, value: &str, style: StyleId, name: String) { if self.get_style(style).stroke_width > 0.0 { let computed_style = self.get_style(style); let mut path_parser = PathParser::from(&*value); @@ -229,8 +261,8 @@ impl Scene { let path = PathIter::new(path); let path = StrokeToFillIter::new(path, StrokeStyle::new(computed_style.stroke_width)); let outline = Outline::from_path_events(path, computed_style); - self.objects.push(PathObject::new(outline, style)); - + self.bounds = self.bounds.union(&outline.bounds); + self.objects.push(PathObject::new(outline, style, name.clone())); } if self.get_style(style).fill_color.is_some() { @@ -238,17 +270,15 @@ impl Scene { let mut path_parser = PathParser::from(&*value); let path = SvgPathToPathEvents::new(&mut path_parser); let outline = Outline::from_path_events(path, computed_style); - self.objects.push(PathObject::new(outline, style)); + self.bounds = self.bounds.union(&outline.bounds); + self.objects.push(PathObject::new(outline, style, name)); } } } impl PathObject { - fn new(outline: Outline, style: StyleId) -> PathObject { - PathObject { - outline, - style, - } + fn new(outline: Outline, style: StyleId, name: String) -> PathObject { + PathObject { outline, style, name } } } @@ -260,7 +290,6 @@ struct Outline { bounds: Rect, } -#[derive(Debug)] struct Contour { points: Vec>, flags: Vec, @@ -287,6 +316,9 @@ impl Outline { let mut current_contour = Contour::new(); 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 { match path_event { PathEvent::MoveTo(to) => { @@ -295,37 +327,37 @@ impl Outline { } current_contour.push_transformed_point(&to, PointFlags::empty(), - &style.transform, + &transform, &mut bounding_points); } PathEvent::LineTo(to) => { current_contour.push_transformed_point(&to, PointFlags::empty(), - &style.transform, + &transform, &mut bounding_points); } PathEvent::QuadraticTo(ctrl, to) => { current_contour.push_transformed_point(&ctrl, PointFlags::CONTROL_POINT_0, - &style.transform, + &transform, &mut bounding_points); current_contour.push_transformed_point(&to, PointFlags::empty(), - &style.transform, + &transform, &mut bounding_points); } PathEvent::CubicTo(ctrl0, ctrl1, to) => { current_contour.push_transformed_point(&ctrl0, PointFlags::CONTROL_POINT_0, - &style.transform, + &transform, &mut bounding_points); current_contour.push_transformed_point(&ctrl1, PointFlags::CONTROL_POINT_1, - &style.transform, + &transform, &mut bounding_points); current_contour.push_transformed_point(&to, PointFlags::empty(), - &style.transform, + &transform, &mut bounding_points); } PathEvent::Close => { @@ -349,7 +381,7 @@ impl Outline { #[inline] fn iter(&self) -> OutlineIter { - OutlineIter { outline: self, index: PointIndex::default() } + OutlineIter { outline: self, contour_iter: None, contour_index: 0 } } fn segment_after(&self, endpoint_index: PointIndex) -> Segment { @@ -359,10 +391,11 @@ impl Outline { impl Contour { fn new() -> Contour { - Contour { - points: vec![], - flags: vec![], - } + Contour { points: vec![], flags: vec![] } + } + + fn iter(&self) -> ContourIter { + ContourIter { contour: self, index: 0 } } fn is_empty(&self) -> bool { @@ -417,8 +450,8 @@ impl Contour { } fn point_is_endpoint(&self, point_index: usize) -> bool { - self.flags[point_index].intersects(PointFlags::CONTROL_POINT_0 | - PointFlags::CONTROL_POINT_1) + !self.flags[point_index].intersects(PointFlags::CONTROL_POINT_0 | + PointFlags::CONTROL_POINT_1) } fn add_to_point_index(&self, point_index: usize, addend: usize) -> usize { @@ -431,6 +464,30 @@ impl Contour { } } +impl Debug for Contour { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("[")?; + if formatter.alternate() { + formatter.write_str("\n")? + } + for (index, segment) in self.iter().enumerate() { + if index > 0 { + formatter.write_str(",")?; + } + if formatter.alternate() { + formatter.write_str("\n ")?; + } else { + formatter.write_str(" ")?; + } + segment.fmt(formatter)?; + } + if formatter.alternate() { + formatter.write_str("\n")? + } + formatter.write_str("]") + } +} + #[derive(Clone, Copy, Debug, Default)] struct PointIndex { contour_index: usize, @@ -439,26 +496,54 @@ struct PointIndex { struct OutlineIter<'a> { outline: &'a Outline, - index: PointIndex, + contour_iter: Option>, + contour_index: usize, +} + +struct ContourIter<'a> { + contour: &'a Contour, + index: usize, } impl<'a> Iterator for OutlineIter<'a> { type Item = PathEvent; fn next(&mut self) -> Option { - if self.index.contour_index == self.outline.contours.len() { + if let Some(ref mut contour_iter) = self.contour_iter { + match contour_iter.next() { + Some(event) => return Some(event), + None => { + self.contour_iter = None; + self.contour_index += 1; + } + } + } + + if self.contour_index == self.outline.contours.len() { return None } - let contour = &self.outline.contours[self.index.contour_index]; - if self.index.point_index == contour.points.len() { - self.index.contour_index += 1; - self.index.point_index = 0; + + self.contour_iter = Some(self.outline.contours[self.contour_index].iter()); + self.next() + } +} + +impl<'a> Iterator for ContourIter<'a> { + type Item = PathEvent; + + fn next(&mut self) -> Option { + let contour = self.contour; + if self.index == contour.points.len() + 1 { + return None + } + if self.index == contour.points.len() { + self.index += 1; return Some(PathEvent::Close) } - let point0_index = self.index.point_index; + let point0_index = self.index; let point0 = contour.points[point0_index]; - self.index.point_index += 1; + self.index += 1; if point0_index == 0 { return Some(PathEvent::MoveTo(point0)) } @@ -466,16 +551,16 @@ impl<'a> Iterator for OutlineIter<'a> { return Some(PathEvent::LineTo(point0)) } - let point1_index = self.index.point_index; + let point1_index = self.index; let point1 = contour.points[point1_index]; - self.index.point_index += 1; + self.index += 1; if contour.point_is_endpoint(point1_index) { return Some(PathEvent::QuadraticTo(point0, point1)) } - let point2_index = self.index.point_index; + let point2_index = self.index; let point2 = contour.points[point2_index]; - self.index.point_index += 1; + self.index += 1; debug_assert!(contour.point_is_endpoint(point2_index)); Some(PathEvent::CubicTo(point0, point1, point2)) } @@ -532,6 +617,16 @@ impl Segment { } } + fn add_to_list(&self, tile_left: f32, primitives: &mut Vec) { + let vector = Vector2D::new(-tile_left, 0.0); + primitives.push(Primitive { + from: self.from + vector, + ctrl0: self.ctrl0 + vector, + ctrl1: self.ctrl1 + vector, + to: self.to + vector, + }) + } + fn is_none(&self) -> bool { !self.flags.contains(SegmentFlags::HAS_ENDPOINTS) } @@ -629,21 +724,30 @@ bitflags! { // Tiling -const TILE_WIDTH: f32 = 4.0; -const TILE_HEIGHT: f32 = 4.0; +const TILE_WIDTH: f32 = 16.0; +const TILE_HEIGHT: f32 = 16.0; -struct Tiler<'a> { - outline: &'a Outline, +struct Tiler<'o, 'p> { + outline: &'o Outline, + primitives: &'p mut Vec, + + view_box: Option>, sorted_edge_indices: Vec, active_intervals: Intervals, active_edges: Vec, } -impl<'a> Tiler<'a> { - fn from_outline(outline: &Outline) -> Tiler { +impl<'o, 'p> Tiler<'o, 'p> { + fn from_outline(outline: &'o Outline, + view_box: &Option>, + primitives: &'p mut Vec) + -> Tiler<'o, 'p> { Tiler { outline, + primitives, + + view_box: *view_box, sorted_edge_indices: vec![], active_intervals: Intervals::new(0.0), @@ -651,7 +755,7 @@ impl<'a> Tiler<'a> { } } - fn generate_tiles(&mut self, strips: &mut Vec) { + fn generate_tiles(&mut self) { // Sort all edge indices. self.sorted_edge_indices.clear(); for contour_index in 0..self.outline.contours.len() { @@ -671,8 +775,13 @@ impl<'a> Tiler<'a> { }); } + // Guard band clipping... let bounds = self.outline.bounds; - let (max_x, max_y) = (bounds.max_x(), bounds.max_y()); + let (mut max_x, mut max_y) = (bounds.max_x(), bounds.max_y()); + if let Some(view_box) = self.view_box { + max_x = clamp(max_x, view_box.origin.x, view_box.max_x()); + max_y = clamp(max_y, view_box.origin.y, view_box.max_y()); + } self.active_intervals.reset(max_x); self.active_edges.clear(); @@ -680,23 +789,39 @@ impl<'a> Tiler<'a> { let mut tile_top = bounds.origin.y - bounds.origin.y % TILE_HEIGHT; while tile_top < max_y { - let mut strip = Strip::new(tile_top); + let tile_extent = Point2D::new(max_x, tile_top + TILE_HEIGHT); + + let above_view_box = match self.view_box { + Some(ref view_box) => tile_extent.y <= view_box.origin.y, + None => false, + }; + //let mut strip = Strip::new(&Point2D::new(tile_left, tile_top)); // TODO(pcwalton): Populate tile strip with active intervals. + // Process old active edges. for active_edge in &mut self.active_edges { - process_active_edge(active_edge, &mut strip, &mut self.active_intervals) + let primitives = if above_view_box { None } else { Some(&mut *self.primitives) }; + process_active_edge(active_edge, + &tile_extent, + primitives, + &mut self.active_intervals) } self.active_edges.retain(|edge| !edge.is_none()); + // Add new active edges. while next_edge_index_index < self.sorted_edge_indices.len() { let mut segment = self.outline.segment_after(self.sorted_edge_indices[next_edge_index_index]); - if segment.min_y() > strip.tile_bottom() { + if segment.min_y() > tile_extent.y { break } - process_active_edge(&mut segment, &mut strip, &mut self.active_intervals); + let primitives = if above_view_box { None } else { Some(&mut *self.primitives) }; + process_active_edge(&mut segment, + &tile_extent, + primitives, + &mut self.active_intervals); if !segment.is_none() { self.active_edges.push(segment); } @@ -704,22 +829,37 @@ impl<'a> Tiler<'a> { next_edge_index_index += 1; } - tile_top = strip.tile_bottom(); - strips.push(strip); + tile_top = tile_extent.y; } } } fn process_active_edge(active_edge: &mut Segment, - strip: &mut Strip, + strip_extent: &Point2D, + primitives: Option<&mut Vec>, active_intervals: &mut Intervals) { - let clipped = active_edge.clip_y(strip.tile_bottom()); + let clipped = active_edge.clip_y(strip_extent.y); if let Some(upper_segment) = clipped.min { - strip.push_segment(upper_segment); + if let Some(primitives) = primitives { + // FIXME(pcwalton): Assumes x-monotonicity! + // FIXME(pcwalton): Don't hardcode a view box left of 0! + let mut min_x = f32::min(upper_segment.from.x, upper_segment.to.x); + let mut max_x = f32::max(upper_segment.from.x, upper_segment.to.x); + min_x = clamp(min_x, 0.0, strip_extent.x); + max_x = clamp(max_x, 0.0, strip_extent.x); + + let mut tile_left = min_x - min_x % TILE_WIDTH; + while tile_left < max_x { + active_edge.add_to_list(tile_left, primitives); + tile_left += TILE_WIDTH; + } + } + // FIXME(pcwalton): Assumes x-monotonicity! - // FIXME(pcwalton): The min call below is a hack! - let from_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.from.x)); - let to_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.to.x)); + let mut from_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.from.x)); + let mut to_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.to.x)); + from_x = clamp(from_x, 0.0, strip_extent.x); + to_x = clamp(to_x, 0.0, strip_extent.x); if from_x < to_x { active_intervals.add(IntervalRange::new(from_x, to_x, -1.0)) } else { @@ -733,28 +873,14 @@ fn process_active_edge(active_edge: &mut Segment, } } -// Strips +// Primitives -struct Strip { - segments: Vec, - tile_top: f32, -} - -impl Strip { - fn new(tile_top: f32) -> Strip { - Strip { - segments: vec![], - tile_top, - } - } - - fn push_segment(&mut self, segment: Segment) { - self.segments.push(segment.translate(&Vector2D::new(0.0, -self.tile_top))) - } - - fn tile_bottom(&self) -> f32 { - self.tile_top + TILE_HEIGHT - } +#[derive(Clone, Copy, Debug)] +struct Primitive { + from: Point2D, + ctrl0: Point2D, + ctrl1: Point2D, + to: Point2D, } // Intervals @@ -970,6 +1096,12 @@ impl<'a, I> Iterator for SvgPathToPathEvents<'a, I> where I: Iterator f32 { + f32::max(f32::min(x, max), min) +} + // Testing #[cfg(test)]