pathfinder/utils/tile-svg/src/main.rs

1973 lines
69 KiB
Rust
Raw Normal View History

2018-12-11 19:38:06 -05:00
// pathfinder/utils/tile-svg/main.rs
//
// Copyright © 2018 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.
#[macro_use]
extern crate bitflags;
2018-12-12 17:55:53 -05:00
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
extern crate rand;
2018-12-17 17:07:19 -05:00
use byteorder::{LittleEndian, WriteBytesExt};
2018-12-16 14:41:43 -05:00
use clap::{App, Arg};
2018-12-18 18:47:47 -05:00
use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D};
2018-12-17 21:31:49 -05:00
use fixedbitset::FixedBitSet;
2018-12-14 17:32:53 -05:00
use jemallocator;
2018-12-14 23:34:36 -05:00
use lyon_geom::cubic_bezier::Flattened;
2018-12-14 21:52:34 -05:00
use lyon_geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment};
2018-12-14 17:21:35 -05:00
use lyon_path::PathEvent;
use lyon_path::iterator::PathIter;
use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter};
2018-12-11 19:38:06 -05:00
use quick_xml::Reader;
2018-12-19 14:16:24 -05:00
use quick_xml::events::{BytesStart, Event};
2018-12-30 12:38:57 -05:00
use rayon::ThreadPoolBuilder;
2018-12-29 20:08:49 -05:00
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
2018-12-13 17:04:17 -05:00
use std::cmp::Ordering;
2018-12-14 21:52:34 -05:00
use std::fmt::{self, Debug, Formatter};
2018-12-17 17:07:19 -05:00
use std::fs::File;
2018-12-19 14:16:24 -05:00
use std::io::{self, BufReader, BufWriter, Write};
2018-12-11 19:38:06 -05:00
use std::mem;
2018-12-14 23:34:36 -05:00
use std::ops::Range;
2018-12-11 19:38:06 -05:00
use std::path::{Path, PathBuf};
use std::str::FromStr;
2018-12-13 17:04:17 -05:00
use std::time::Instant;
2018-12-28 10:30:54 -05:00
use std::u32;
2018-12-11 19:38:06 -05:00
use svgtypes::{Color as SvgColor, PathParser, PathSegment as SvgPathSegment, TransformListParser};
use svgtypes::{TransformListToken};
2018-12-14 17:32:53 -05:00
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
2018-12-14 21:52:34 -05:00
// TODO(pcwalton): Make this configurable.
2018-12-17 17:07:19 -05:00
const SCALE_FACTOR: f32 = 1.0;
2018-12-14 21:52:34 -05:00
2018-12-14 23:34:36 -05:00
// TODO(pcwalton): Make this configurable.
const FLATTENING_TOLERANCE: f32 = 3.0;
2018-12-11 19:38:06 -05:00
fn main() {
2018-12-16 14:41:43 -05:00
let matches =
App::new("tile-svg").arg(Arg::with_name("runs").short("r")
.long("runs")
.value_name("COUNT")
.takes_value(true)
.help("Run a benchmark with COUNT runs"))
2018-12-30 12:38:57 -05:00
.arg(Arg::with_name("jobs").short("j")
.long("jobs")
.value_name("THREADS")
.takes_value(true)
.help("Number of threads to use"))
2018-12-16 14:41:43 -05:00
.arg(Arg::with_name("INPUT").help("Path to the SVG file to render")
.required(true)
.index(1))
.arg(Arg::with_name("OUTPUT").help("Path to the output PF3 data")
.required(false)
.index(2))
.get_matches();
let runs: usize = match matches.value_of("runs") {
Some(runs) => runs.parse().unwrap(),
None => 1,
};
2018-12-30 12:38:57 -05:00
let jobs: Option<usize> = matches.value_of("jobs").map(|string| string.parse().unwrap());
2018-12-17 17:07:19 -05:00
let input_path = PathBuf::from(matches.value_of("INPUT").unwrap());
let output_path = matches.value_of("OUTPUT").map(PathBuf::from);
2018-12-16 14:41:43 -05:00
2018-12-30 12:38:57 -05:00
// Set up Rayon.
let mut thread_pool_builder = ThreadPoolBuilder::new();
if let Some(jobs) = jobs {
thread_pool_builder = thread_pool_builder.num_threads(jobs);
}
thread_pool_builder.build_global().unwrap();
2018-12-17 17:07:19 -05:00
let scene = Scene::from_path(&input_path);
println!("Scene bounds: {:?}", scene.bounds);
2018-12-13 17:04:17 -05:00
let start_time = Instant::now();
2018-12-28 10:30:54 -05:00
let mut built_scene = BuiltScene::new(&scene.view_box, scene.objects.len() as u32);
2018-12-16 14:41:43 -05:00
for _ in 0..runs {
2018-12-30 12:38:57 -05:00
let built_objects = match jobs {
Some(1) => scene.build_objects_sequentially(),
_ => scene.build_objects(),
2018-12-29 20:08:49 -05:00
};
built_scene = BuiltScene::from_objects(&scene.view_box, &built_objects);
2018-12-13 18:53:58 -05:00
}
2018-12-13 17:04:17 -05:00
let elapsed_time = Instant::now() - start_time;
2018-12-17 17:07:19 -05:00
2018-12-13 18:53:58 -05:00
let elapsed_ms = elapsed_time.as_secs() as f64 * 1000.0 +
elapsed_time.subsec_micros() as f64 / 1000.0;
2018-12-16 20:43:03 -05:00
println!("{:.3}ms elapsed", elapsed_ms / runs as f64);
println!("{} fill primitives generated", built_scene.fills.len());
2018-12-17 21:31:49 -05:00
println!("{} tiles ({} solid, {} mask) generated",
2018-12-19 18:58:54 -05:00
built_scene.solid_tiles.len() + built_scene.mask_tiles.len(),
built_scene.solid_tiles.len(),
built_scene.mask_tiles.len());
2018-12-17 17:07:19 -05:00
2018-12-19 18:58:54 -05:00
/*
2018-12-19 14:16:24 -05:00
println!("solid tiles:");
2018-12-19 18:58:54 -05:00
for (index, tile) in built_scene.solid_tiles.iter().enumerate() {
println!("... {}: {:?}", index, tile);
2018-12-19 14:16:24 -05:00
}
2018-12-19 21:20:22 -05:00
println!("fills:");
for (index, fill) in built_scene.fills.iter().enumerate() {
println!("... {}: {:?}", index, fill);
}
2018-12-19 18:58:54 -05:00
*/
2018-12-19 14:16:24 -05:00
2018-12-17 17:07:19 -05:00
if let Some(output_path) = output_path {
built_scene.write(&mut BufWriter::new(File::create(output_path).unwrap())).unwrap();
}
2018-12-11 19:38:06 -05:00
}
#[derive(Debug)]
struct Scene {
objects: Vec<PathObject>,
styles: Vec<ComputedStyle>,
2018-12-14 21:52:34 -05:00
bounds: Rect<f32>,
2018-12-21 17:10:26 -05:00
view_box: Rect<f32>,
2018-12-11 19:38:06 -05:00
}
#[derive(Debug)]
struct PathObject {
outline: Outline,
style: StyleId,
2018-12-16 20:43:03 -05:00
color: ColorU,
2018-12-14 21:52:34 -05:00
name: String,
2018-12-11 19:38:06 -05:00
}
2018-12-16 14:41:43 -05:00
#[derive(Debug)]
struct ComputedStyle {
fill_color: Option<SvgColor>,
stroke_width: f32,
stroke_color: Option<SvgColor>,
transform: Transform2D<f32>,
}
#[derive(Default)]
struct GroupStyle {
fill_color: Option<SvgColor>,
stroke_width: Option<f32>,
stroke_color: Option<SvgColor>,
transform: Option<Transform2D<f32>>,
}
impl ComputedStyle {
fn new() -> ComputedStyle {
ComputedStyle {
fill_color: None,
stroke_width: 1.0,
stroke_color: None,
transform: Transform2D::identity(),
}
}
}
2018-12-11 19:38:06 -05:00
#[derive(Clone, Copy, PartialEq, Debug)]
struct StyleId(u32);
impl Scene {
fn new() -> Scene {
2018-12-21 17:10:26 -05:00
Scene { objects: vec![], styles: vec![], bounds: Rect::zero(), view_box: Rect::zero() }
2018-12-11 19:38:06 -05:00
}
fn from_path(path: &Path) -> Scene {
let mut reader = Reader::from_file(&path).unwrap();
2018-12-14 21:52:34 -05:00
let global_transform = Transform2D::create_scale(SCALE_FACTOR, SCALE_FACTOR);
2018-12-11 19:38:06 -05:00
let mut xml_buffer = vec![];
let mut group_styles = vec![];
let mut style = None;
let mut scene = Scene::new();
loop {
match reader.read_event(&mut xml_buffer) {
Ok(Event::Start(ref event)) |
Ok(Event::Empty(ref event)) if event.name() == b"path" => {
2018-12-19 14:16:24 -05:00
scene.push_group_style(&mut reader, event, &mut group_styles, &mut style);
2018-12-11 19:38:06 -05:00
let attributes = event.attributes();
2018-12-14 21:52:34 -05:00
let (mut encoded_path, mut name) = (String::new(), String::new());
2018-12-11 19:38:06 -05:00
for attribute in attributes {
let attribute = attribute.unwrap();
2018-12-14 21:52:34 -05:00
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();
2018-12-11 19:38:06 -05:00
}
}
2018-12-19 14:16:24 -05:00
let computed_style = scene.ensure_style(&mut style, &mut group_styles);
scene.push_svg_path(&encoded_path, computed_style, name);
group_styles.pop();
style = None;
2018-12-11 19:38:06 -05:00
}
2018-12-14 21:52:34 -05:00
2018-12-11 19:38:06 -05:00
Ok(Event::Start(ref event)) if event.name() == b"g" => {
2018-12-19 14:16:24 -05:00
scene.push_group_style(&mut reader, event, &mut group_styles, &mut style);
}
2018-12-14 21:52:34 -05:00
2018-12-19 14:16:24 -05:00
Ok(Event::End(ref event)) if event.name() == b"g" => {
group_styles.pop();
2018-12-11 19:38:06 -05:00
style = None;
}
2018-12-14 21:52:34 -05:00
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()));
2018-12-21 17:10:26 -05:00
scene.view_box = global_transform.transform_rect(&view_box);
2018-12-14 21:52:34 -05:00
}
}
}
2018-12-11 19:38:06 -05:00
Ok(Event::Eof) | Err(_) => break,
Ok(_) => {}
}
xml_buffer.clear();
}
return scene;
}
2018-12-19 14:16:24 -05:00
fn push_group_style(&mut self,
reader: &mut Reader<BufReader<File>>,
event: &BytesStart,
group_styles: &mut Vec<GroupStyle>,
style: &mut Option<StyleId>) {
let mut group_style = GroupStyle::default();
let attributes = event.attributes();
for attribute in attributes {
let attribute = attribute.unwrap();
match attribute.key {
b"fill" => {
let value = reader.decode(&attribute.value);
if let Ok(color) = SvgColor::from_str(&value) {
group_style.fill_color = Some(color);
}
}
b"stroke" => {
let value = reader.decode(&attribute.value);
if let Ok(color) = SvgColor::from_str(&value) {
group_style.stroke_color = Some(color)
}
}
b"transform" => {
let value = reader.decode(&attribute.value);
let mut current_transform = Transform2D::identity();
let transform_list_parser = TransformListParser::from(&*value);
for transform in transform_list_parser {
match transform {
Ok(TransformListToken::Matrix { a, b, c, d, e, f }) => {
let transform: Transform2D<f32> =
Transform2D::row_major(a, b, c, d, e, f).cast();
current_transform = current_transform.pre_mul(&transform)
}
_ => {}
}
}
group_style.transform = Some(current_transform);
}
b"stroke-width" => {
if let Ok(width) = reader.decode(&attribute.value).parse() {
group_style.stroke_width = Some(width)
}
}
_ => {}
}
}
group_styles.push(group_style);
*style = None;
}
2018-12-11 19:38:06 -05:00
fn ensure_style(&mut self, current_style: &mut Option<StyleId>, group_styles: &[GroupStyle])
-> StyleId {
if let Some(current_style) = *current_style {
return current_style
}
let mut computed_style = ComputedStyle::new();
for group_style in group_styles {
if let Some(fill_color) = group_style.fill_color {
computed_style.fill_color = Some(fill_color)
}
if let Some(stroke_width) = group_style.stroke_width {
computed_style.stroke_width = stroke_width
}
if let Some(stroke_color) = group_style.stroke_color {
computed_style.stroke_color = Some(stroke_color)
}
if let Some(transform) = group_style.transform {
computed_style.transform = computed_style.transform.pre_mul(&transform)
}
}
let id = StyleId(self.styles.len() as u32);
self.styles.push(computed_style);
id
}
2018-12-11 19:53:57 -05:00
fn get_style(&self, style: StyleId) -> &ComputedStyle {
&self.styles[style.0 as usize]
}
2018-12-13 17:04:17 -05:00
2018-12-30 12:38:57 -05:00
fn build_shader(&self, object_index: u32) -> ObjectShader {
ObjectShader {
fill_color: self.objects[object_index as usize].color,
}
}
// This function exists to make profiling easier.
2018-12-29 20:08:49 -05:00
fn build_objects_sequentially(&self) -> Vec<BuiltObject> {
self.objects.iter().enumerate().map(|(object_index, object)| {
2018-12-30 12:38:57 -05:00
let mut tiler = Tiler::new(&object.outline,
object_index as u32,
&self.view_box,
&self.build_shader(object_index as u32));
2018-12-14 21:52:34 -05:00
tiler.generate_tiles();
tiler.built_object
}).collect()
2018-12-13 17:04:17 -05:00
}
2018-12-14 17:21:35 -05:00
2018-12-30 12:38:57 -05:00
fn build_objects(&self) -> Vec<BuiltObject> {
2018-12-29 20:08:49 -05:00
self.objects.par_iter().enumerate().map(|(object_index, object)| {
2018-12-30 12:38:57 -05:00
let mut tiler = Tiler::new(&object.outline,
object_index as u32,
&self.view_box,
&self.build_shader(object_index as u32));
2018-12-29 20:08:49 -05:00
tiler.generate_tiles();
tiler.built_object
}).collect()
}
2018-12-14 21:52:34 -05:00
fn push_svg_path(&mut self, value: &str, style: StyleId, name: String) {
2018-12-22 00:04:48 -05:00
if self.get_style(style).stroke_color.is_some() {
2018-12-14 17:21:35 -05:00
let computed_style = self.get_style(style);
let mut path_parser = PathParser::from(&*value);
let path = SvgPathToPathEvents::new(&mut path_parser);
let path = PathIter::new(path);
let path = StrokeToFillIter::new(path, StrokeStyle::new(computed_style.stroke_width));
2018-12-18 19:16:51 -05:00
let path = MonotonicConversionIter::new(path);
2018-12-14 17:21:35 -05:00
let outline = Outline::from_path_events(path, computed_style);
2018-12-16 20:43:03 -05:00
let color = match computed_style.stroke_color {
None => ColorU::black(),
Some(color) => ColorU::from_svg_color(color),
};
2018-12-14 21:52:34 -05:00
self.bounds = self.bounds.union(&outline.bounds);
2018-12-16 20:43:03 -05:00
self.objects.push(PathObject::new(outline, color, style, name.clone()));
2018-12-14 17:21:35 -05:00
}
if self.get_style(style).fill_color.is_some() {
let computed_style = self.get_style(style);
let mut path_parser = PathParser::from(&*value);
let path = SvgPathToPathEvents::new(&mut path_parser);
2018-12-18 19:16:51 -05:00
let path = MonotonicConversionIter::new(path);
2018-12-14 17:21:35 -05:00
let outline = Outline::from_path_events(path, computed_style);
2018-12-16 20:43:03 -05:00
let color = match computed_style.fill_color {
None => ColorU::black(),
Some(color) => ColorU::from_svg_color(color),
};
2018-12-14 21:52:34 -05:00
self.bounds = self.bounds.union(&outline.bounds);
2018-12-16 20:43:03 -05:00
self.objects.push(PathObject::new(outline, color, style, name));
2018-12-14 17:21:35 -05:00
}
}
2018-12-11 19:38:06 -05:00
}
impl PathObject {
2018-12-16 20:43:03 -05:00
fn new(outline: Outline, color: ColorU, style: StyleId, name: String) -> PathObject {
PathObject { outline, color, style, name }
2018-12-11 19:38:06 -05:00
}
}
// Outlines
#[derive(Debug)]
struct Outline {
contours: Vec<Contour>,
2018-12-13 17:04:17 -05:00
bounds: Rect<f32>,
2018-12-11 19:38:06 -05:00
}
struct Contour {
points: Vec<Point2D<f32>>,
flags: Vec<PointFlags>,
}
bitflags! {
struct PointFlags: u8 {
const CONTROL_POINT_0 = 0x01;
const CONTROL_POINT_1 = 0x02;
}
}
impl Outline {
fn new() -> Outline {
Outline {
contours: vec![],
2018-12-13 17:04:17 -05:00
bounds: Rect::zero(),
2018-12-11 19:38:06 -05:00
}
}
2018-12-14 17:21:35 -05:00
fn from_path_events<I>(path_events: I, style: &ComputedStyle) -> Outline
where I: Iterator<Item = PathEvent> {
2018-12-11 19:38:06 -05:00
let mut outline = Outline::new();
let mut current_contour = Contour::new();
2018-12-13 17:04:17 -05:00
let mut bounding_points = None;
2018-12-14 21:52:34 -05:00
let global_transform = Transform2D::create_scale(SCALE_FACTOR, SCALE_FACTOR);
let transform = global_transform.pre_mul(&style.transform);
2018-12-14 17:21:35 -05:00
for path_event in path_events {
match path_event {
PathEvent::MoveTo(to) => {
2018-12-11 19:38:06 -05:00
if !current_contour.is_empty() {
outline.contours.push(mem::replace(&mut current_contour, Contour::new()))
}
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&to,
PointFlags::empty(),
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:38:06 -05:00
}
2018-12-14 17:21:35 -05:00
PathEvent::LineTo(to) => {
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&to,
PointFlags::empty(),
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:38:06 -05:00
}
2018-12-14 17:21:35 -05:00
PathEvent::QuadraticTo(ctrl, to) => {
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&ctrl,
PointFlags::CONTROL_POINT_0,
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&to,
PointFlags::empty(),
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:38:06 -05:00
}
2018-12-14 17:21:35 -05:00
PathEvent::CubicTo(ctrl0, ctrl1, to) => {
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&ctrl0,
PointFlags::CONTROL_POINT_0,
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&ctrl1,
PointFlags::CONTROL_POINT_1,
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:53:57 -05:00
current_contour.push_transformed_point(&to,
PointFlags::empty(),
2018-12-14 21:52:34 -05:00
&transform,
2018-12-13 17:04:17 -05:00
&mut bounding_points);
2018-12-11 19:38:06 -05:00
}
2018-12-14 17:21:35 -05:00
PathEvent::Close => {
2018-12-11 19:38:06 -05:00
if !current_contour.is_empty() {
outline.contours.push(mem::replace(&mut current_contour, Contour::new()));
}
}
2018-12-14 17:21:35 -05:00
PathEvent::Arc(..) => unimplemented!("arcs"),
2018-12-11 19:38:06 -05:00
}
}
if !current_contour.is_empty() {
outline.contours.push(current_contour)
}
2018-12-13 17:04:17 -05:00
if let Some((upper_left, lower_right)) = bounding_points {
outline.bounds = Rect::from_points([upper_left, lower_right].into_iter())
}
2018-12-14 17:21:35 -05:00
outline
}
2018-12-11 19:38:06 -05:00
}
impl Contour {
fn new() -> Contour {
2018-12-14 21:52:34 -05:00
Contour { points: vec![], flags: vec![] }
}
fn iter(&self) -> ContourIter {
ContourIter { contour: self, index: 0 }
2018-12-11 19:38:06 -05:00
}
fn is_empty(&self) -> bool {
self.points.is_empty()
}
2018-12-11 19:53:57 -05:00
2018-12-21 20:58:39 -05:00
fn len(&self) -> u32 {
self.points.len() as u32
}
fn position_of(&self, index: u32) -> Point2D<f32> {
self.points[index as usize]
}
2018-12-11 19:53:57 -05:00
fn push_transformed_point(&mut self,
point: &Point2D<f32>,
flags: PointFlags,
2018-12-13 17:04:17 -05:00
transform: &Transform2D<f32>,
bounding_points: &mut Option<(Point2D<f32>, Point2D<f32>)>) {
let point = transform.transform_point(point);
self.points.push(point);
2018-12-11 19:53:57 -05:00
self.flags.push(flags);
2018-12-13 17:04:17 -05:00
match *bounding_points {
Some((ref mut upper_left, ref mut lower_right)) => {
*upper_left = upper_left.min(point);
*lower_right = lower_right.max(point);
}
None => *bounding_points = Some((point, point)),
}
}
2018-12-21 20:58:39 -05:00
fn segment_after(&self, point_index: u32) -> Segment {
2018-12-13 17:04:17 -05:00
debug_assert!(self.point_is_endpoint(point_index));
2018-12-13 19:22:06 -05:00
let mut segment = Segment::new();
2018-12-21 20:58:39 -05:00
segment.from = self.position_of(point_index);
2018-12-13 19:22:06 -05:00
segment.flags |= SegmentFlags::HAS_ENDPOINTS;
2018-12-13 17:04:17 -05:00
let point1_index = self.add_to_point_index(point_index, 1);
if self.point_is_endpoint(point1_index) {
2018-12-21 20:58:39 -05:00
segment.to = self.position_of(point1_index);
2018-12-13 19:22:06 -05:00
} else {
2018-12-21 20:58:39 -05:00
segment.ctrl0 = self.position_of(point1_index);
2018-12-13 19:22:06 -05:00
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) {
2018-12-21 20:58:39 -05:00
segment.to = self.position_of(point2_index);
2018-12-13 19:22:06 -05:00
} else {
2018-12-21 20:58:39 -05:00
segment.ctrl1 = self.position_of(point2_index);
2018-12-13 19:22:06 -05:00
segment.flags |= SegmentFlags::HAS_CONTROL_POINT_1;
let point3_index = self.add_to_point_index(point_index, 3);
2018-12-21 20:58:39 -05:00
segment.to = self.position_of(point3_index);
2018-12-13 19:22:06 -05:00
}
2018-12-13 17:04:17 -05:00
}
2018-12-13 19:22:06 -05:00
segment
2018-12-13 17:04:17 -05:00
}
2018-12-21 20:58:39 -05:00
fn point_is_endpoint(&self, point_index: u32) -> bool {
!self.flags[point_index as usize].intersects(PointFlags::CONTROL_POINT_0 |
PointFlags::CONTROL_POINT_1)
2018-12-13 17:04:17 -05:00
}
2018-12-21 20:58:39 -05:00
fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 {
let (index, limit) = (point_index + addend, self.len());
2018-12-13 19:22:06 -05:00
if index >= limit {
index - limit
} else {
index
}
2018-12-13 17:04:17 -05:00
}
2018-12-20 14:39:01 -05:00
2018-12-21 20:58:39 -05:00
fn point_is_logically_above(&self, a: u32, b: u32) -> bool {
let (a_y, b_y) = (self.points[a as usize].y, self.points[b as usize].y);
a_y < b_y || (a_y == b_y && a < b)
2018-12-20 14:39:01 -05:00
}
2018-12-21 20:58:39 -05:00
fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 {
2018-12-20 14:39:01 -05:00
loop {
point_index = self.prev_point_index_of(point_index);
if self.point_is_endpoint(point_index) {
return point_index
}
}
}
2018-12-21 20:58:39 -05:00
fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 {
2018-12-20 14:39:01 -05:00
loop {
point_index = self.next_point_index_of(point_index);
if self.point_is_endpoint(point_index) {
return point_index
}
}
}
2018-12-21 20:58:39 -05:00
fn prev_point_index_of(&self, point_index: u32) -> u32 {
2018-12-20 14:39:01 -05:00
if point_index == 0 {
2018-12-21 20:58:39 -05:00
self.len() - 1
2018-12-20 14:39:01 -05:00
} else {
point_index - 1
}
}
2018-12-21 20:58:39 -05:00
fn next_point_index_of(&self, point_index: u32) -> u32 {
if point_index == self.len() - 1 {
2018-12-20 14:39:01 -05:00
0
} else {
point_index + 1
}
}
2018-12-13 17:04:17 -05:00
}
2018-12-14 21:52:34 -05:00
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("]")
}
}
2018-12-20 16:09:08 -05:00
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
2018-12-21 20:58:39 -05:00
struct PointIndex(u32);
impl PointIndex {
fn new(contour: u32, point: u32) -> PointIndex {
PointIndex((contour << 20) | point)
}
fn contour(self) -> u32 {
self.0 >> 20
}
fn point(self) -> u32 {
self.0 & 0x000fffff
}
2018-12-13 17:04:17 -05:00
}
2018-12-14 21:52:34 -05:00
struct ContourIter<'a> {
contour: &'a Contour,
2018-12-21 20:58:39 -05:00
index: u32,
2018-12-14 17:21:35 -05:00
}
2018-12-14 21:52:34 -05:00
impl<'a> Iterator for ContourIter<'a> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
let contour = self.contour;
2018-12-21 20:58:39 -05:00
if self.index == contour.len() + 1 {
2018-12-14 17:21:35 -05:00
return None
}
2018-12-21 20:58:39 -05:00
if self.index == contour.len() {
2018-12-14 21:52:34 -05:00
self.index += 1;
2018-12-14 17:21:35 -05:00
return Some(PathEvent::Close)
}
2018-12-14 21:52:34 -05:00
let point0_index = self.index;
2018-12-21 20:58:39 -05:00
let point0 = contour.position_of(point0_index);
2018-12-14 21:52:34 -05:00
self.index += 1;
2018-12-14 17:21:35 -05:00
if point0_index == 0 {
return Some(PathEvent::MoveTo(point0))
}
if contour.point_is_endpoint(point0_index) {
return Some(PathEvent::LineTo(point0))
}
2018-12-14 21:52:34 -05:00
let point1_index = self.index;
2018-12-21 20:58:39 -05:00
let point1 = contour.position_of(point1_index);
2018-12-14 21:52:34 -05:00
self.index += 1;
2018-12-14 17:21:35 -05:00
if contour.point_is_endpoint(point1_index) {
return Some(PathEvent::QuadraticTo(point0, point1))
}
2018-12-14 21:52:34 -05:00
let point2_index = self.index;
2018-12-21 20:58:39 -05:00
let point2 = contour.position_of(point2_index);
2018-12-14 21:52:34 -05:00
self.index += 1;
2018-12-14 17:21:35 -05:00
debug_assert!(contour.point_is_endpoint(point2_index));
Some(PathEvent::CubicTo(point0, point1, point2))
}
}
2018-12-13 17:04:17 -05:00
#[derive(Clone, Copy, Debug, PartialEq)]
2018-12-13 19:22:06 -05:00
struct Segment {
from: Point2D<f32>,
ctrl0: Point2D<f32>,
ctrl1: Point2D<f32>,
to: Point2D<f32>,
flags: SegmentFlags,
2018-12-13 17:04:17 -05:00
}
impl Segment {
2018-12-13 19:22:06 -05:00
fn new() -> Segment {
Segment {
from: Point2D::zero(),
ctrl0: Point2D::zero(),
ctrl1: Point2D::zero(),
to: Point2D::zero(),
flags: SegmentFlags::empty(),
}
}
fn from_line(line: &LineSegment<f32>) -> Segment {
Segment {
from: line.from,
ctrl0: Point2D::zero(),
ctrl1: Point2D::zero(),
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
2018-12-13 18:53:58 -05:00
}
}
2018-12-13 19:22:06 -05:00
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,
2018-12-13 17:04:17 -05:00
}
}
2018-12-18 18:47:47 -05:00
fn as_line_segment(&self) -> Option<LineSegment<f32>> {
if !self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
Some(LineSegment { from: self.from, to: self.to })
} else {
None
}
}
// FIXME(pcwalton): We should basically never use this function.
fn as_cubic_segment(&self) -> Option<CubicBezierSegment<f32>> {
if !self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
None
} else if !self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
Some((QuadraticBezierSegment {
from: self.from,
ctrl: self.ctrl0,
to: self.to,
}).to_cubic())
} else {
Some(CubicBezierSegment {
from: self.from,
ctrl1: self.ctrl0,
ctrl2: self.ctrl1,
to: self.to,
})
}
}
2018-12-18 20:08:02 -05:00
fn is_degenerate(&self) -> bool {
return f32::abs(self.to.x - self.from.x) < EPSILON ||
f32::abs(self.to.y - self.from.y) < EPSILON;
const EPSILON: f32 = 0.0001;
}
2018-12-18 18:47:47 -05:00
fn clip_x(&self, range: Range<f32>) -> Option<Segment> {
2018-12-18 20:11:43 -05:00
// Trivial cases.
if (self.from.x <= range.start && self.to.x <= range.start) ||
(self.from.x >= range.end && self.to.x >= range.end) {
2018-12-18 20:08:02 -05:00
return None
}
2018-12-18 20:11:43 -05:00
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)
2018-12-18 20:08:02 -05:00
}
// 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
}
2018-12-18 18:47:47 -05:00
}
2018-12-18 20:08:02 -05:00
let clipped = Segment::from_line(&line_segment);
return Some(clipped);
2018-12-18 18:47:47 -05:00
}
// TODO(pcwalton): Don't degree elevate!
2018-12-18 20:08:02 -05:00
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
}
2018-12-18 18:47:47 -05:00
}
2018-12-18 20:08:02 -05:00
let clipped = Segment::from_cubic(&cubic_segment);
2018-12-18 18:47:47 -05:00
return Some(clipped);
}
2018-12-19 13:04:58 -05:00
fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) {
// Trivial cases.
if self.from.y <= y && self.to.y <= y {
2018-12-19 21:20:22 -05:00
return (Some(*self), None)
2018-12-19 13:04:58 -05:00
}
if self.from.y >= y && self.to.y >= y {
2018-12-19 21:20:22 -05:00
return (None, Some(*self))
2018-12-19 13:04:58 -05:00
}
// TODO(pcwalton): Reduce code duplication?
2018-12-19 21:20:22 -05:00
let (prev, next) = match self.as_line_segment() {
Some(line_segment) => {
let t = LineAxis::from_y(&line_segment).solve_for_t(y).unwrap();
let (prev, next) = line_segment.split(t);
(Segment::from_line(&prev), Segment::from_line(&next))
}
None => {
// TODO(pcwalton): Don't degree elevate!
2018-12-21 14:33:02 -05:00
let cubic_segment = self.as_cubic_segment().unwrap();
2018-12-19 21:20:22 -05:00
let t = CubicAxis::from_y(&cubic_segment).solve_for_t(y);
let t = t.expect("Failed to solve cubic for Y!");
let (prev, next) = cubic_segment.split(t);
(Segment::from_cubic(&prev), Segment::from_cubic(&next))
}
};
2018-12-19 13:04:58 -05:00
2018-12-19 21:20:22 -05:00
if self.from.y < self.to.y {
(Some(prev), Some(next))
} else {
(Some(next), Some(prev))
}
2018-12-19 13:04:58 -05:00
}
2018-12-20 10:53:36 -05:00
#[inline(never)]
fn generate_fill_primitives(&self, built_object: &mut BuiltObject, tile_y: i16) {
if let Some(line_segment) = self.as_line_segment() {
generate_fill_primitives_for_line(line_segment, built_object, tile_y);
2018-12-18 18:47:47 -05:00
return;
}
// TODO(pcwalton): Don't degree elevate!
let segment = self.as_cubic_segment().unwrap();
2018-12-14 23:34:36 -05:00
let flattener = Flattened::new(segment, FLATTENING_TOLERANCE);
let mut from = self.from;
for to in flattener {
generate_fill_primitives_for_line(LineSegment { from, to }, built_object, tile_y);
2018-12-14 23:34:36 -05:00
from = to;
}
2018-12-18 18:47:47 -05:00
fn generate_fill_primitives_for_line(mut segment: LineSegment<f32>,
built_object: &mut BuiltObject,
tile_y: i16) {
let winding = segment.from.x > segment.to.x;
let (segment_left, segment_right) = if !winding {
(segment.from.x, segment.to.x)
} else {
(segment.to.x, segment.from.x)
};
2018-12-21 14:33:02 -05:00
let segment_tile_left = f32::floor(segment_left / TILE_WIDTH) as i16;
let segment_tile_right = f32::ceil(segment_right / TILE_WIDTH) as i16;
for subsegment_tile_x in segment_tile_left..segment_tile_right {
let (mut fill_from, mut fill_to) = (segment.from, segment.to);
let subsegment_tile_right = (subsegment_tile_x + 1) as f32 * TILE_WIDTH;
if subsegment_tile_right < segment_right {
let x = subsegment_tile_right;
let point = Point2D::new(x, segment.solve_y_for_x(x));
if !winding {
fill_to = point;
segment.from = point;
} else {
fill_from = point;
segment.to = point;
}
}
2018-12-18 18:47:47 -05:00
built_object.add_fill(&fill_from, &fill_to, subsegment_tile_x, tile_y);
2018-12-18 18:47:47 -05:00
}
}
2018-12-14 21:52:34 -05:00
}
2018-12-13 19:22:06 -05:00
fn is_none(&self) -> bool {
!self.flags.contains(SegmentFlags::HAS_ENDPOINTS)
}
2018-12-20 17:57:16 -05:00
fn min_x(&self) -> f32 { f32::min(self.from.x, self.to.x) }
2018-12-21 20:45:16 -05:00
fn max_x(&self) -> f32 { f32::max(self.from.x, self.to.x) }
2018-12-20 17:57:16 -05:00
fn winding(&self) -> i32 {
match self.from.x.partial_cmp(&self.to.x) {
Some(Ordering::Less) => -1,
Some(Ordering::Greater) => 1,
Some(Ordering::Equal) | None => 0,
}
2018-12-13 17:04:17 -05:00
}
}
2018-12-13 19:22:06 -05:00
bitflags! {
struct SegmentFlags: u8 {
const HAS_ENDPOINTS = 0x01;
const HAS_CONTROL_POINT_0 = 0x02;
const HAS_CONTROL_POINT_1 = 0x04;
}
}
2018-12-12 17:55:53 -05:00
// Tiling
2018-12-14 21:52:34 -05:00
const TILE_WIDTH: f32 = 16.0;
const TILE_HEIGHT: f32 = 16.0;
2018-12-13 17:04:17 -05:00
struct Tiler<'o> {
2018-12-14 21:52:34 -05:00
outline: &'o Outline,
2018-12-26 18:44:43 -05:00
object_index: u32,
built_object: BuiltObject,
2018-12-14 21:52:34 -05:00
2018-12-21 17:10:26 -05:00
view_box: Rect<f32>,
bounds: Rect<f32>,
2018-12-13 18:53:58 -05:00
2018-12-20 18:26:29 -05:00
point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>,
2018-12-21 16:46:28 -05:00
old_active_edges: Vec<ActiveEdge>,
2018-12-12 17:55:53 -05:00
}
impl<'o> Tiler<'o> {
2018-12-30 12:38:57 -05:00
fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>, shader: &ObjectShader)
-> Tiler<'o> {
let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero());
2018-12-30 12:38:57 -05:00
let built_object = BuiltObject::new(&bounds, shader);
2018-12-12 17:55:53 -05:00
Tiler {
outline,
2018-12-26 18:44:43 -05:00
object_index,
built_object,
2018-12-14 21:52:34 -05:00
view_box: *view_box,
bounds,
2018-12-13 18:53:58 -05:00
2018-12-20 18:26:29 -05:00
point_queue: SortedVector::new(),
active_edges: SortedVector::new(),
2018-12-21 16:46:28 -05:00
old_active_edges: vec![],
2018-12-12 17:55:53 -05:00
}
}
2018-12-13 17:04:17 -05:00
2018-12-20 16:09:08 -05:00
#[inline(never)]
2018-12-14 21:52:34 -05:00
fn generate_tiles(&mut self) {
2018-12-20 16:09:08 -05:00
// Initialize the point queue.
2018-12-20 15:47:40 -05:00
self.init_point_queue();
2018-12-13 17:04:17 -05:00
// Reset active edges.
2018-12-13 18:53:58 -05:00
self.active_edges.clear();
2018-12-21 16:46:28 -05:00
self.old_active_edges.clear();
2018-12-20 18:26:29 -05:00
2018-12-17 21:31:49 -05:00
// Generate strips.
let tile_rect = self.built_object.tile_rect;
for strip_origin_y in tile_rect.origin.y..tile_rect.max_y() {
self.generate_strip(strip_origin_y);
2018-12-21 16:46:28 -05:00
}
2018-12-30 17:07:39 -05:00
//println!("{:#?}", self.built_object);
2018-12-21 16:46:28 -05:00
}
2018-12-20 17:57:16 -05:00
2018-12-21 16:46:28 -05:00
#[inline(never)]
fn generate_strip(&mut self, strip_origin_y: i16) {
2018-12-21 16:46:28 -05:00
// Process old active edges.
self.process_old_active_edges(strip_origin_y);
2018-12-20 17:57:16 -05:00
2018-12-21 16:46:28 -05:00
// Add new active edges.
let strip_max_y = (strip_origin_y + 1) as f32 * TILE_HEIGHT;
while let Some(queued_endpoint) = self.point_queue.peek() {
if queued_endpoint.y >= strip_max_y {
break
2018-12-21 16:46:28 -05:00
}
self.add_new_active_edge(strip_origin_y);
2018-12-21 16:46:28 -05:00
}
}
2018-12-20 18:26:29 -05:00
2018-12-21 16:46:28 -05:00
#[inline(never)]
fn process_old_active_edges(&mut self, tile_y: i16) {
2018-12-30 17:07:39 -05:00
let tile_origin_y = tile_y as f32 * TILE_HEIGHT;
let mut current_tile_x = self.built_object.tile_rect.origin.x;
let mut current_subtile_x = 0.0;
let mut current_winding = 0;
2018-12-21 16:46:28 -05:00
debug_assert!(self.old_active_edges.is_empty());
2018-12-21 16:46:28 -05:00
mem::swap(&mut self.old_active_edges, &mut self.active_edges.array);
2018-12-30 19:04:42 -05:00
let mut last_segment_x = -9999.0;
2018-12-21 16:46:28 -05:00
for mut active_edge in self.old_active_edges.drain(..) {
// Determine x-intercept and winding.
2018-12-21 20:45:16 -05:00
let (segment_x, edge_winding) =
if active_edge.segment.from.y < active_edge.segment.to.y {
(active_edge.segment.from.x, 1)
} else {
(active_edge.segment.to.x, -1)
};
2018-12-30 19:04:42 -05:00
/*
println!("tile Y {}: segment_x={} edge_winding={} current_tile_x={} current_subtile_x={} current_winding={}",
tile_y,
segment_x,
edge_winding,
current_tile_x,
current_subtile_x,
current_winding);
*/
// FIXME(pcwalton): Remove this debug code!
debug_assert!(segment_x >= last_segment_x);
last_segment_x = segment_x;
// Do initial subtile fill, if necessary.
2018-12-30 12:38:57 -05:00
let segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16;
2018-12-30 19:04:42 -05:00
if current_tile_x < segment_tile_x && current_subtile_x > 0.0 {
let current_x = (current_tile_x as f32) * TILE_WIDTH + current_subtile_x;
let left = Point2D::new(current_x, tile_origin_y);
let right = Point2D::new((current_tile_x + 1) as f32 * TILE_WIDTH, tile_origin_y);
if current_winding != 0 {
self.built_object.add_fill(if current_winding < 0 { &left } else { &right },
if current_winding < 0 { &right } else { &left },
current_tile_x,
tile_y);
/*
println!("... emitting initial fill {} -> {} winding {} @ tile {}",
left.x, right.x, current_winding, current_tile_x);
*/
}
current_tile_x += 1;
current_subtile_x = 0.0;
}
// Move over to the correct tile, filling in as we go.
while current_tile_x < segment_tile_x {
2018-12-30 19:04:42 -05:00
//println!("... emitting backdrop {} @ tile {}", current_winding, current_tile_x);
self.built_object.get_tile_mut(current_tile_x, tile_y).backdrop = current_winding;
current_tile_x += 1;
current_subtile_x = 0.0;
2018-12-13 17:04:17 -05:00
}
2018-12-20 18:26:29 -05:00
2018-12-30 19:04:42 -05:00
// Do final subtile fill, if necessary.
debug_assert!(current_tile_x == segment_tile_x);
debug_assert!(current_tile_x < self.built_object.tile_rect.max_x());
2018-12-30 19:04:42 -05:00
let segment_subtile_x = segment_x - (current_tile_x as f32) * TILE_WIDTH;
if segment_subtile_x >= current_subtile_x {
let current_x = (current_tile_x as f32) * TILE_WIDTH + current_subtile_x;
2018-12-30 17:07:39 -05:00
let left = Point2D::new(current_x, tile_origin_y);
let right = Point2D::new(segment_x, tile_origin_y);
2018-12-30 19:04:42 -05:00
if current_winding != 0 {
/*
println!("... emitting final fill {} -> {} winding {} @ tile {}",
left.x, right.x, current_winding, current_tile_x);
*/
self.built_object.add_fill(if current_winding < 0 { &left } else { &right },
if current_winding < 0 { &right } else { &left },
current_tile_x,
tile_y);
}
current_subtile_x = segment_subtile_x;
2018-12-21 16:46:28 -05:00
}
2018-12-13 17:04:17 -05:00
2018-12-21 16:46:28 -05:00
// Update winding.
current_winding += edge_winding;
2018-12-13 17:04:17 -05:00
2018-12-21 16:46:28 -05:00
// Process the edge.
process_active_edge(&mut active_edge.segment, &mut self.built_object, tile_y);
2018-12-21 16:46:28 -05:00
if !active_edge.segment.is_none() {
self.active_edges.push(active_edge);
2018-12-13 17:04:17 -05:00
}
2018-12-21 16:46:28 -05:00
}
}
2018-12-13 17:04:17 -05:00
2018-12-21 16:46:28 -05:00
#[inline(never)]
fn add_new_active_edge(&mut self, tile_y: i16) {
2018-12-21 16:46:28 -05:00
let outline = &self.outline;
let point_index = self.point_queue.pop().unwrap().point_index;
2018-12-21 20:58:39 -05:00
let contour = &outline.contours[point_index.contour() as usize];
2018-12-21 16:46:28 -05:00
2018-12-21 17:10:26 -05:00
// TODO(pcwalton): Could use a bitset of processed edges…
2018-12-21 20:58:39 -05:00
let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point());
let next_endpoint_index = contour.next_endpoint_index_of(point_index.point());
if contour.point_is_logically_above(point_index.point(), prev_endpoint_index) {
2018-12-21 16:46:28 -05:00
process_active_segment(contour,
2018-12-21 20:58:39 -05:00
prev_endpoint_index,
&mut self.active_edges,
&mut self.built_object,
tile_y);
2018-12-21 16:46:28 -05:00
self.point_queue.push(QueuedEndpoint {
2018-12-21 20:58:39 -05:00
point_index: PointIndex::new(point_index.contour(), prev_endpoint_index),
y: contour.position_of(prev_endpoint_index).y,
2018-12-21 16:46:28 -05:00
});
}
2018-12-19 18:58:54 -05:00
2018-12-21 20:58:39 -05:00
if contour.point_is_logically_above(point_index.point(), next_endpoint_index) {
2018-12-21 16:46:28 -05:00
process_active_segment(contour,
point_index.point(),
&mut self.active_edges,
&mut self.built_object,
tile_y);
2018-12-21 16:46:28 -05:00
self.point_queue.push(QueuedEndpoint {
2018-12-21 20:58:39 -05:00
point_index: PointIndex::new(point_index.contour(), next_endpoint_index),
y: contour.position_of(next_endpoint_index).y,
2018-12-21 16:46:28 -05:00
});
}
}
2018-12-20 15:47:40 -05:00
#[inline(never)]
fn init_point_queue(&mut self) {
// Find MIN points.
self.point_queue.clear();
for (contour_index, contour) in self.outline.contours.iter().enumerate() {
2018-12-21 20:58:39 -05:00
let contour_index = contour_index as u32;
2018-12-20 15:47:40 -05:00
let mut cur_endpoint_index = 0;
let mut prev_endpoint_index = contour.prev_endpoint_index_of(cur_endpoint_index);
let mut next_endpoint_index = contour.next_endpoint_index_of(cur_endpoint_index);
while cur_endpoint_index < next_endpoint_index {
if contour.point_is_logically_above(cur_endpoint_index, prev_endpoint_index) &&
contour.point_is_logically_above(cur_endpoint_index, next_endpoint_index) {
2018-12-20 16:09:08 -05:00
self.point_queue.push(QueuedEndpoint {
2018-12-21 20:58:39 -05:00
point_index: PointIndex::new(contour_index, cur_endpoint_index),
y: contour.position_of(cur_endpoint_index).y,
2018-12-20 15:47:40 -05:00
});
}
prev_endpoint_index = cur_endpoint_index;
cur_endpoint_index = next_endpoint_index;
next_endpoint_index = contour.next_endpoint_index_of(cur_endpoint_index);
}
}
}
2018-12-13 18:53:58 -05:00
}
2018-12-13 17:04:17 -05:00
2018-12-20 14:39:01 -05:00
fn process_active_segment(contour: &Contour,
2018-12-21 20:58:39 -05:00
from_endpoint_index: u32,
2018-12-20 18:26:29 -05:00
active_edges: &mut SortedVector<ActiveEdge>,
built_object: &mut BuiltObject,
tile_y: i16) {
let mut segment = contour.segment_after(from_endpoint_index);
2018-12-20 14:39:01 -05:00
if segment.is_degenerate() {
return
}
process_active_edge(&mut segment, built_object, tile_y);
2018-12-20 14:39:01 -05:00
if !segment.is_none() {
2018-12-20 17:57:16 -05:00
active_edges.push(ActiveEdge::new(segment));
2018-12-20 14:39:01 -05:00
}
}
fn process_active_edge(active_edge: &mut Segment, built_object: &mut BuiltObject, tile_y: i16) {
// Chop the segment.
2018-12-19 21:20:22 -05:00
// TODO(pcwalton): Maybe these shouldn't be Options?
let (upper_segment, lower_segment) = active_edge.split_y((tile_y + 1) as f32 * TILE_HEIGHT);
2018-12-16 20:43:03 -05:00
// Add fill primitives for upper part.
2018-12-19 21:20:22 -05:00
if let Some(segment) = upper_segment {
segment.generate_fill_primitives(built_object, tile_y);
2018-12-19 21:20:22 -05:00
}
// Queue lower part.
*active_edge = lower_segment.unwrap_or(Segment::new());
2018-12-13 17:04:17 -05:00
}
// Scene construction
2018-12-26 18:44:43 -05:00
impl BuiltScene {
fn new(view_box: &Rect<f32>, object_count: u32) -> BuiltScene {
BuiltScene {
view_box: *view_box,
fills: vec![],
solid_tiles: vec![],
mask_tiles: vec![],
2018-12-30 12:38:57 -05:00
shaders: vec![ObjectShader::default(); object_count as usize],
tile_rect: round_rect_out_to_tile_bounds(view_box),
2018-12-28 10:30:54 -05:00
}
}
2018-12-28 10:30:54 -05:00
#[inline(never)]
fn from_objects(view_box: &Rect<f32>, objects: &[BuiltObject]) -> BuiltScene {
let mut scene = BuiltScene::new(view_box, objects.len() as u32);
let mut z_buffer = FixedBitSet::with_capacity(scene.tile_rect.size.width as usize *
scene.tile_rect.size.height as usize);
let mut object_tile_index_to_scene_mask_tile_index = vec![];
for (object_index, object) in objects.iter().enumerate().rev() {
object_tile_index_to_scene_mask_tile_index.clear();
object_tile_index_to_scene_mask_tile_index.reserve(object.tiles.len());
// Copy tiles.
for (tile_index, tile) in object.tiles.iter().enumerate() {
let scene_tile_index = scene.scene_tile_index(tile.tile_x, tile.tile_y);
if z_buffer[scene_tile_index as usize] {
// Occluded.
object_tile_index_to_scene_mask_tile_index.push(u32::MAX);
} else if object.mask_tiles[tile_index] {
// Visible mask tile.
let scene_mask_tile_index = scene.mask_tiles.len() as u32;
object_tile_index_to_scene_mask_tile_index.push(scene_mask_tile_index);
scene.mask_tiles.push(MaskTileScenePrimitive {
tile: *tile,
object_index: object_index as u32,
});
} else {
// Visible transparent or solid tile.
object_tile_index_to_scene_mask_tile_index.push(u32::MAX);
if tile.backdrop != 0 {
scene.solid_tiles.push(SolidTileScenePrimitive {
tile_x: tile.tile_x,
tile_y: tile.tile_y,
object_index: object_index as u32,
});
z_buffer.insert(scene_tile_index as usize);
}
}
2018-12-28 10:30:54 -05:00
}
// Remap and copy fills, culling as necessary.
for fill in &object.fills {
let object_tile_index = object.tile_coords_to_index(fill.tile_x, fill.tile_y);
match object_tile_index_to_scene_mask_tile_index[object_tile_index as usize] {
u32::MAX => {}
scene_mask_tile_index => {
scene.fills.push(FillScenePrimitive {
from: fill.from,
to: fill.to,
mask_tile_index: scene_mask_tile_index,
})
}
}
}
2018-12-30 17:07:39 -05:00
// Copy shader.
scene.shaders[object_index as usize] = object.shader;
}
scene
2018-12-28 10:30:54 -05:00
}
fn scene_tile_index(&self, tile_x: i16, tile_y: i16) -> u32 {
(tile_y - self.tile_rect.origin.y) as u32 * self.tile_rect.size.width as u32 +
(tile_x - self.tile_rect.origin.x) as u32
}
2018-12-26 18:44:43 -05:00
}
2018-12-14 21:52:34 -05:00
// Primitives
2018-12-13 17:04:17 -05:00
#[derive(Debug)]
struct BuiltObject {
bounds: Rect<f32>,
tile_rect: Rect<i16>,
tiles: Vec<TileObjectPrimitive>,
fills: Vec<FillObjectPrimitive>,
mask_tiles: FixedBitSet,
2018-12-30 12:38:57 -05:00
shader: ObjectShader,
}
2018-12-16 20:43:03 -05:00
#[derive(Debug)]
struct BuiltScene {
2018-12-21 17:10:26 -05:00
view_box: Rect<f32>,
fills: Vec<FillScenePrimitive>,
solid_tiles: Vec<SolidTileScenePrimitive>,
mask_tiles: Vec<MaskTileScenePrimitive>,
2018-12-30 12:38:57 -05:00
shaders: Vec<ObjectShader>,
tile_rect: Rect<i16>,
2018-12-16 20:43:03 -05:00
}
2018-12-14 21:52:34 -05:00
#[derive(Clone, Copy, Debug)]
struct FillObjectPrimitive {
2018-12-14 21:52:34 -05:00
from: Point2D<f32>,
to: Point2D<f32>,
tile_x: i16,
tile_y: i16,
2018-12-16 20:43:03 -05:00
}
#[derive(Clone, Copy, Debug)]
struct TileObjectPrimitive {
2018-12-21 15:00:29 -05:00
tile_x: i16,
tile_y: i16,
backdrop: i32,
}
#[derive(Clone, Copy, Debug)]
struct FillScenePrimitive {
from: Point2D<f32>,
to: Point2D<f32>,
mask_tile_index: u32,
2018-12-19 18:58:54 -05:00
}
#[derive(Clone, Copy, Debug)]
struct SolidTileScenePrimitive {
2018-12-21 15:00:29 -05:00
tile_x: i16,
tile_y: i16,
2018-12-26 18:44:43 -05:00
object_index: u32,
}
#[derive(Clone, Copy, Debug)]
struct MaskTileScenePrimitive {
tile: TileObjectPrimitive,
object_index: u32,
2018-12-16 20:43:03 -05:00
}
2018-12-30 12:38:57 -05:00
#[derive(Clone, Copy, Debug, Default)]
struct ObjectShader {
fill_color: ColorU,
}
#[derive(Clone, Copy, Debug, Default)]
2018-12-16 20:43:03 -05:00
struct ColorU {
r: u8,
g: u8,
b: u8,
a: u8,
}
// Utilities for built objects
impl BuiltObject {
2018-12-30 12:38:57 -05:00
fn new(bounds: &Rect<f32>, shader: &ObjectShader) -> BuiltObject {
// Compute the tile rect.
let tile_rect = round_rect_out_to_tile_bounds(&bounds);
// Allocate tiles.
let tile_count = tile_rect.size.width as usize * tile_rect.size.height as usize;
let mut tiles = Vec::with_capacity(tile_count);
for y in tile_rect.origin.y..tile_rect.max_y() {
for x in tile_rect.origin.x..tile_rect.max_x() {
tiles.push(TileObjectPrimitive::new(x, y));
}
}
BuiltObject {
bounds: *bounds,
tile_rect,
tiles,
2018-12-28 10:30:54 -05:00
fills: vec![],
mask_tiles: FixedBitSet::with_capacity(tile_count),
2018-12-30 12:38:57 -05:00
shader: *shader,
2018-12-28 10:30:54 -05:00
}
2018-12-16 20:43:03 -05:00
}
2018-12-17 17:07:19 -05:00
fn add_fill(&mut self, from: &Point2D<f32>, to: &Point2D<f32>, tile_x: i16, tile_y: i16) {
2018-12-30 17:07:39 -05:00
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);
2018-12-30 17:07:39 -05:00
self.fills.push(FillObjectPrimitive {
from: *from - tile_origin,
to: *to - tile_origin,
tile_x,
tile_y,
});
self.mask_tiles.insert(tile_index as usize);
}
// FIXME(pcwalton): Use a `Point2D<i16>` instead?
fn tile_coords_to_index(&self, tile_x: i16, tile_y: i16) -> u32 {
/*println!("tile_coords_to_index(x={}, y={}, tile_rect={:?})",
tile_x,
tile_y,
self.tile_rect);*/
(tile_y - self.tile_rect.origin.y) as u32 * self.tile_rect.size.width as u32 +
(tile_x - self.tile_rect.origin.x) as u32
}
fn get_tile_mut(&mut self, tile_x: i16, tile_y: i16) -> &mut TileObjectPrimitive {
let tile_index = self.tile_coords_to_index(tile_x, tile_y);
&mut self.tiles[tile_index as usize]
}
}
// Scene serialization
impl BuiltScene {
2018-12-17 17:07:19 -05:00
fn write<W>(&self, writer: &mut W) -> io::Result<()> where W: Write {
writer.write_all(b"RIFF")?;
2018-12-21 17:10:26 -05:00
let header_size = 4 * 4;
let fill_size = self.fills.len() * mem::size_of::<FillScenePrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTileScenePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTileScenePrimitive>();
2018-12-30 12:38:57 -05:00
let shaders_size = self.shaders.len() * mem::size_of::<ObjectShader>();
2018-12-17 21:31:49 -05:00
writer.write_u32::<LittleEndian>((4 +
2018-12-21 17:10:26 -05:00
8 + header_size +
2018-12-17 21:31:49 -05:00
8 + fill_size +
2018-12-19 18:58:54 -05:00
8 + solid_tiles_size +
2018-12-30 12:38:57 -05:00
8 + mask_tiles_size +
8 + shaders_size) as u32)?;
2018-12-17 17:07:19 -05:00
writer.write_all(b"PF3S")?;
2018-12-21 17:10:26 -05:00
writer.write_all(b"head")?;
writer.write_u32::<LittleEndian>(header_size as u32)?;
writer.write_f32::<LittleEndian>(self.view_box.origin.x)?;
writer.write_f32::<LittleEndian>(self.view_box.origin.y)?;
writer.write_f32::<LittleEndian>(self.view_box.size.width)?;
writer.write_f32::<LittleEndian>(self.view_box.size.height)?;
2018-12-17 17:07:19 -05:00
writer.write_all(b"fill")?;
writer.write_u32::<LittleEndian>(fill_size as u32)?;
for fill_primitive in &self.fills {
write_point(writer, &fill_primitive.from)?;
write_point(writer, &fill_primitive.to)?;
writer.write_u32::<LittleEndian>(fill_primitive.mask_tile_index)?;
2018-12-17 17:07:19 -05:00
}
2018-12-19 18:58:54 -05:00
writer.write_all(b"soli")?;
writer.write_u32::<LittleEndian>(solid_tiles_size as u32)?;
for &tile_primitive in &self.solid_tiles {
2018-12-21 15:00:29 -05:00
writer.write_i16::<LittleEndian>(tile_primitive.tile_x)?;
writer.write_i16::<LittleEndian>(tile_primitive.tile_y)?;
2018-12-26 18:44:43 -05:00
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
2018-12-17 17:07:19 -05:00
}
2018-12-17 21:31:49 -05:00
writer.write_all(b"mask")?;
2018-12-19 18:58:54 -05:00
writer.write_u32::<LittleEndian>(mask_tiles_size as u32)?;
for &tile_primitive in &self.mask_tiles {
writer.write_i16::<LittleEndian>(tile_primitive.tile.tile_x)?;
writer.write_i16::<LittleEndian>(tile_primitive.tile.tile_y)?;
writer.write_i32::<LittleEndian>(tile_primitive.tile.backdrop)?;
2018-12-26 18:44:43 -05:00
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
2018-12-17 21:31:49 -05:00
}
2018-12-30 12:38:57 -05:00
writer.write_all(b"shad")?;
writer.write_u32::<LittleEndian>(shaders_size as u32)?;
for &shader in &self.shaders {
let fill_color = shader.fill_color;
writer.write_all(&[fill_color.r, fill_color.g, fill_color.b, fill_color.a])?;
}
2018-12-17 17:07:19 -05:00
return Ok(());
fn write_point<W>(writer: &mut W, point: &Point2D<f32>) -> io::Result<()> where W: Write {
writer.write_f32::<LittleEndian>(point.x)?;
writer.write_f32::<LittleEndian>(point.y)?;
Ok(())
}
}
2018-12-16 20:43:03 -05:00
}
impl SolidTileScenePrimitive {
fn new(tile_x: i16, tile_y: i16, object_index: u32) -> SolidTileScenePrimitive {
SolidTileScenePrimitive { tile_x, tile_y, object_index }
2018-12-19 18:58:54 -05:00
}
}
impl TileObjectPrimitive {
fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive {
TileObjectPrimitive { tile_x, tile_y, backdrop: 0 }
2018-12-16 20:43:03 -05:00
}
}
impl ColorU {
fn black() -> ColorU {
ColorU { r: 0, g: 0, b: 0, a: 255 }
}
fn from_svg_color(svg_color: SvgColor) -> ColorU {
ColorU { r: svg_color.red, g: svg_color.green, b: svg_color.blue, a: 255 }
}
2018-12-12 17:55:53 -05:00
}
// Tile geometry utilities
fn round_rect_out_to_tile_bounds(rect: &Rect<f32>) -> Rect<i16> {
let tile_origin = Point2D::new(f32::floor(rect.origin.x / TILE_WIDTH) as i16,
f32::floor(rect.origin.y / TILE_HEIGHT) as i16);
let tile_extent = Point2D::new(f32::ceil(rect.max_x() / TILE_WIDTH) as i16,
f32::ceil(rect.max_y() / TILE_HEIGHT) as i16);
let tile_size = Size2D::new(tile_extent.x - tile_origin.x, tile_extent.y - tile_origin.y);
Rect::new(tile_origin, tile_size)
}
2018-12-14 17:21:35 -05:00
// SVG stuff
struct SvgPathToPathEvents<'a, I> where I: Iterator<Item = SvgPathSegment> {
iter: &'a mut I,
2018-12-20 10:53:36 -05:00
last_endpoint: Point2D<f32>,
2018-12-14 17:21:35 -05:00
last_ctrl_point: Option<Point2D<f32>>,
}
impl<'a, I> SvgPathToPathEvents<'a, I> where I: Iterator<Item = SvgPathSegment> {
fn new(iter: &'a mut I) -> SvgPathToPathEvents<'a, I> {
2018-12-20 10:53:36 -05:00
SvgPathToPathEvents { iter, last_endpoint: Point2D::zero(), last_ctrl_point: None }
2018-12-14 17:21:35 -05:00
}
}
impl<'a, I> Iterator for SvgPathToPathEvents<'a, I> where I: Iterator<Item = SvgPathSegment> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
return match self.iter.next() {
None => None,
Some(SvgPathSegment::MoveTo { abs, x, y }) => {
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
self.last_ctrl_point = None;
Some(PathEvent::MoveTo(to))
}
Some(SvgPathSegment::LineTo { abs, x, y }) => {
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
self.last_ctrl_point = None;
Some(PathEvent::LineTo(to))
}
Some(SvgPathSegment::HorizontalLineTo { abs, x }) => {
let to = compute_point(x, 0.0, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
self.last_ctrl_point = None;
Some(PathEvent::LineTo(to))
}
Some(SvgPathSegment::VerticalLineTo { abs, y }) => {
let to = compute_point(0.0, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
self.last_ctrl_point = None;
Some(PathEvent::LineTo(to))
}
Some(SvgPathSegment::Quadratic { abs, x1, y1, x, y }) => {
let ctrl = compute_point(x1, y1, abs, &self.last_endpoint);
self.last_ctrl_point = Some(ctrl);
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
Some(PathEvent::QuadraticTo(ctrl, to))
}
Some(SvgPathSegment::SmoothQuadratic { abs, x, y }) => {
2018-12-20 10:53:36 -05:00
let ctrl = reflect_point(&self.last_endpoint, &self.last_ctrl_point);
2018-12-14 17:21:35 -05:00
self.last_ctrl_point = Some(ctrl);
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
Some(PathEvent::QuadraticTo(ctrl, to))
}
Some(SvgPathSegment::CurveTo { abs, x1, y1, x2, y2, x, y }) => {
let ctrl0 = compute_point(x1, y1, abs, &self.last_endpoint);
let ctrl1 = compute_point(x2, y2, abs, &self.last_endpoint);
self.last_ctrl_point = Some(ctrl1);
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
}
Some(SvgPathSegment::SmoothCurveTo { abs, x2, y2, x, y }) => {
2018-12-20 10:53:36 -05:00
let ctrl0 = reflect_point(&self.last_endpoint, &self.last_ctrl_point);
2018-12-14 17:21:35 -05:00
let ctrl1 = compute_point(x2, y2, abs, &self.last_endpoint);
self.last_ctrl_point = Some(ctrl1);
let to = compute_point(x, y, abs, &self.last_endpoint);
2018-12-20 10:53:36 -05:00
self.last_endpoint = to;
2018-12-14 17:21:35 -05:00
Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
}
Some(SvgPathSegment::ClosePath { abs: _ }) => {
2018-12-20 10:53:36 -05:00
// FIXME(pcwalton): Current endpoint becomes path initial point!
self.last_ctrl_point = None;
2018-12-14 17:21:35 -05:00
Some(PathEvent::Close)
}
Some(SvgPathSegment::EllipticalArc { .. }) => unimplemented!("arcs"),
};
2018-12-20 10:53:36 -05:00
fn compute_point(x: f64, y: f64, abs: bool, last_endpoint: &Point2D<f32>)
2018-12-14 17:21:35 -05:00
-> Point2D<f32> {
let point = Point2D::new(x, y).to_f32();
2018-12-20 10:53:36 -05:00
if !abs {
*last_endpoint + point.to_vector()
} else {
point
}
}
fn reflect_point(last_endpoint: &Point2D<f32>, last_ctrl_point: &Option<Point2D<f32>>)
-> Point2D<f32> {
match *last_ctrl_point {
Some(ref last_ctrl_point) => {
let vector = *last_endpoint - *last_ctrl_point;
*last_endpoint + vector
}
None => *last_endpoint,
2018-12-14 17:21:35 -05:00
}
}
}
}
2018-12-18 19:16:51 -05:00
// Monotonic conversion utilities
// TODO(pcwalton): I think we only need to be monotonic in Y, maybe?
struct MonotonicConversionIter<I> where I: Iterator<Item = PathEvent> {
inner: I,
buffer: Option<PathEvent>,
last_point: Point2D<f32>,
}
impl<I> Iterator for MonotonicConversionIter<I> where I: Iterator<Item = PathEvent> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
if self.buffer.is_none() {
match self.inner.next() {
None => return None,
Some(event) => self.buffer = Some(event),
}
}
match self.buffer.take().unwrap() {
PathEvent::MoveTo(to) => {
self.last_point = to;
Some(PathEvent::MoveTo(to))
}
PathEvent::LineTo(to) => {
self.last_point = to;
Some(PathEvent::LineTo(to))
}
PathEvent::CubicTo(ctrl0, ctrl1, to) => {
let segment = CubicBezierSegment {
from: self.last_point,
ctrl1: ctrl0,
ctrl2: ctrl1,
to,
};
if segment.is_monotonic() {
self.last_point = to;
return Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
}
// FIXME(pcwalton): O(n^2)!
let mut t = None;
segment.for_each_monotonic_t(|split_t| {
if t.is_none() {
t = Some(split_t)
}
});
let t = t.unwrap();
if t_is_too_close_to_zero_or_one(t) {
self.last_point = to;
return Some(PathEvent::CubicTo(ctrl0, ctrl1, to))
}
let (prev, next) = segment.split(t);
self.last_point = next.from;
self.buffer = Some(PathEvent::CubicTo(next.ctrl1, next.ctrl2, next.to));
return Some(PathEvent::CubicTo(prev.ctrl1, prev.ctrl2, prev.to));
}
PathEvent::QuadraticTo(ctrl, to) => {
let segment = QuadraticBezierSegment { from: self.last_point, ctrl: ctrl, to };
if segment.is_monotonic() {
self.last_point = to;
return Some(PathEvent::QuadraticTo(ctrl, to))
}
// FIXME(pcwalton): O(n^2)!
let mut t = None;
segment.for_each_monotonic_t(|split_t| {
if t.is_none() {
t = Some(split_t)
}
});
let t = t.unwrap();
if t_is_too_close_to_zero_or_one(t) {
self.last_point = to;
return Some(PathEvent::QuadraticTo(ctrl, to))
}
let (prev, next) = segment.split(t);
self.last_point = next.from;
self.buffer = Some(PathEvent::QuadraticTo(next.ctrl, next.to));
return Some(PathEvent::QuadraticTo(prev.ctrl, prev.to));
}
PathEvent::Close => Some(PathEvent::Close),
PathEvent::Arc(a, b, c, d) => {
// FIXME(pcwalton): Make these monotonic too.
return Some(PathEvent::Arc(a, b, c, d))
}
}
}
}
impl<I> MonotonicConversionIter<I> where I: Iterator<Item = PathEvent> {
fn new(inner: I) -> MonotonicConversionIter<I> {
MonotonicConversionIter {
inner,
buffer: None,
last_point: Point2D::zero(),
}
}
}
2018-12-18 20:08:02 -05:00
// Path utilities
trait SolveT {
fn sample(&self, t: f32) -> f32;
fn sample_deriv(&self, t: f32) -> f32;
2018-12-19 14:16:24 -05:00
// TODO(pcwalton): Use Brent's method.
2018-12-18 20:08:02 -05:00
fn solve_for_t(&self, x: f32) -> Option<f32> {
2018-12-19 14:16:24 -05:00
const MAX_ITERATIONS: u32 = 64;
const TOLERANCE: f32 = 0.001;
let (mut min, mut max) = (0.0, 1.0);
let (mut x_min, x_max) = (self.sample(min) - x, self.sample(max) - x);
if (x_min < 0.0 && x_max < 0.0) || (x_min > 0.0 && x_max > 0.0) {
return None
2018-12-18 20:08:02 -05:00
}
2018-12-19 14:16:24 -05:00
let mut iteration = 0;
loop {
let mid = lerp(min, max, 0.5);
if iteration >= MAX_ITERATIONS || (max - min) * 0.5 < TOLERANCE {
return Some(mid)
}
let x_mid = self.sample(mid) - x;
if x_mid == 0.0 {
return Some(mid)
}
if (x_min < 0.0 && x_mid < 0.0) || (x_min > 0.0 && x_mid > 0.0) {
min = mid;
x_min = x_mid;
} else {
max = mid;
}
iteration += 1;
2018-12-18 20:08:02 -05:00
}
}
}
// FIXME(pcwalton): This is probably dumb and inefficient.
struct LineAxis { from: f32, to: f32 }
impl LineAxis {
fn from_x(segment: &LineSegment<f32>) -> LineAxis {
LineAxis { from: segment.from.x, to: segment.to.x }
}
fn from_y(segment: &LineSegment<f32>) -> LineAxis {
LineAxis { from: segment.from.y, to: segment.to.y }
}
}
impl SolveT for LineAxis {
fn sample(&self, t: f32) -> f32 {
lerp(self.from, self.to, t)
}
2018-12-21 14:33:02 -05:00
fn sample_deriv(&self, _: f32) -> f32 {
2018-12-18 20:08:02 -05:00
self.to - self.from
}
}
struct QuadraticAxis { from: f32, ctrl: f32, to: f32 }
impl QuadraticAxis {
fn from_x(segment: &QuadraticBezierSegment<f32>) -> QuadraticAxis {
QuadraticAxis { from: segment.from.x, ctrl: segment.ctrl.x, to: segment.to.x }
}
fn from_y(segment: &QuadraticBezierSegment<f32>) -> QuadraticAxis {
QuadraticAxis { from: segment.from.y, ctrl: segment.ctrl.y, to: segment.to.y }
}
}
impl SolveT for QuadraticAxis {
fn sample(&self, t: f32) -> f32 {
lerp(lerp(self.from, self.ctrl, t), lerp(self.ctrl, self.to, t), t)
}
fn sample_deriv(&self, t: f32) -> f32 {
2018-12-21 14:34:10 -05:00
2.0 * lerp(self.ctrl - self.from, self.to - self.ctrl, t)
2018-12-18 20:08:02 -05:00
}
}
struct CubicAxis { from: f32, ctrl0: f32, ctrl1: f32, to: f32 }
impl CubicAxis {
fn from_x(segment: &CubicBezierSegment<f32>) -> CubicAxis {
CubicAxis {
from: segment.from.x,
ctrl0: segment.ctrl1.x,
ctrl1: segment.ctrl2.x,
to: segment.to.x,
}
}
fn from_y(segment: &CubicBezierSegment<f32>) -> CubicAxis {
CubicAxis {
from: segment.from.y,
ctrl0: segment.ctrl1.y,
ctrl1: segment.ctrl2.y,
to: segment.to.y,
}
}
}
impl SolveT for CubicAxis {
fn sample(&self, t: f32) -> f32 {
2018-12-29 21:29:26 -05:00
let b3 = self.to + 3.0 * (self.ctrl0 - self.ctrl1) - self.from;
let b2 = 3.0 * (self.from - 2.0 * self.ctrl0 + self.ctrl1) + b3 * t;
let b1 = 3.0 * (self.ctrl0 - self.from) + b2 * t;
let b0 = self.from + b1 * t;
b0
2018-12-18 20:08:02 -05:00
}
fn sample_deriv(&self, t: f32) -> f32 {
let inv_t = 1.0 - t;
3.0 * inv_t * inv_t * (self.ctrl0 - self.from) +
6.0 * inv_t * t * (self.ctrl1 - self.ctrl0) +
3.0 * t * t * (self.to - self.ctrl1)
}
}
2018-12-20 18:26:29 -05:00
// SortedVector
#[derive(Clone, Debug)]
pub struct SortedVector<T> where T: PartialOrd {
array: Vec<T>,
}
impl<T> SortedVector<T> where T: PartialOrd {
fn new() -> SortedVector<T> {
SortedVector { array: vec![] }
}
fn push(&mut self, value: T) {
self.array.push(value);
let mut index = self.array.len() - 1;
while index > 0 {
index -= 1;
if self.array[index] <= self.array[index + 1] {
break
}
self.array.swap(index, index + 1);
}
}
2018-12-21 14:33:02 -05:00
fn peek(&self) -> Option<&T> { self.array.last() }
fn pop(&mut self) -> Option<T> { self.array.pop() }
fn clear(&mut self) { self.array.clear() }
2018-12-20 13:54:22 -05:00
2018-12-21 14:33:02 -05:00
#[allow(dead_code)]
fn is_empty(&self) -> bool { self.array.is_empty() }
2018-12-20 13:54:22 -05:00
}
2018-12-20 17:57:16 -05:00
// Queued endpoints
2018-12-20 16:09:08 -05:00
#[derive(PartialEq)]
struct QueuedEndpoint {
point_index: PointIndex,
y: f32,
}
impl Eq for QueuedEndpoint {}
impl PartialOrd<QueuedEndpoint> for QueuedEndpoint {
fn partial_cmp(&self, other: &QueuedEndpoint) -> Option<Ordering> {
2018-12-20 18:26:29 -05:00
// NB: Reversed!
2018-12-21 20:58:39 -05:00
(other.y, other.point_index).partial_cmp(&(self.y, self.point_index))
2018-12-20 16:09:08 -05:00
}
}
2018-12-20 17:57:16 -05:00
// Active edges
#[derive(Clone, PartialEq, Debug)]
struct ActiveEdge {
segment: Segment,
}
impl ActiveEdge {
fn new(segment: Segment) -> ActiveEdge {
ActiveEdge { segment }
}
}
impl PartialOrd<ActiveEdge> for ActiveEdge {
fn partial_cmp(&self, other: &ActiveEdge) -> Option<Ordering> {
2018-12-21 20:45:16 -05:00
let this_x = if self.segment.from.y < self.segment.to.y {
self.segment.from.x
} else {
self.segment.to.x
};
let other_x = if other.segment.from.y < other.segment.to.y {
other.segment.from.x
} else {
other.segment.to.x
};
this_x.partial_cmp(&other_x)
2018-12-20 17:57:16 -05:00
}
}
2018-12-14 21:52:34 -05:00
// Trivial utilities
2018-12-18 20:08:02 -05:00
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
2018-12-14 21:52:34 -05:00
fn clamp(x: f32, min: f32, max: f32) -> f32 {
f32::max(f32::min(x, max), min)
}
2018-12-18 19:16:51 -05:00
fn t_is_too_close_to_zero_or_one(t: f32) -> bool {
const EPSILON: f32 = 0.001;
t < EPSILON || t > 1.0 - EPSILON
}
2018-12-14 17:21:35 -05:00
// Testing
2018-12-12 17:55:53 -05:00
#[cfg(test)]
mod test {
2018-12-21 14:33:02 -05:00
use crate::SortedVector;
use quickcheck;
2018-12-12 17:55:53 -05:00
2018-12-20 18:26:29 -05:00
#[test]
fn test_sorted_vec() {
quickcheck::quickcheck(prop_sorted_vec as fn(Vec<i32>) -> bool);
fn prop_sorted_vec(mut values: Vec<i32>) -> bool {
let mut sorted_vec = SortedVector::new();
for &value in &values {
sorted_vec.push(value)
}
values.sort();
let mut results = Vec::with_capacity(values.len());
while !sorted_vec.is_empty() {
results.push(sorted_vec.pop().unwrap());
}
results.reverse();
assert_eq!(&values, &results);
true
}
}
2018-12-12 17:55:53 -05:00
}