Rewrite to handle objects independently

This commit is contained in:
Patrick Walton 2018-12-29 17:00:50 -08:00
parent aaa8f26a5c
commit abb062bf71
1 changed files with 267 additions and 392 deletions

View File

@ -77,8 +77,7 @@ fn main() {
let start_time = Instant::now();
let mut built_scene = BuiltScene::new(&scene.view_box, scene.objects.len() as u32);
for _ in 0..runs {
built_scene = scene.build();
built_scene = merge_and_cull_subscenes(&[&built_scene]);
built_scene = BuiltScene::from_objects(&scene.view_box, &scene.build_objects());
}
let elapsed_time = Instant::now() - start_time;
@ -312,17 +311,13 @@ impl Scene {
&self.styles[style.0 as usize]
}
fn build(&self) -> BuiltScene {
let mut built_scene = BuiltScene::new(&self.view_box, self.objects.len() as u32);
for (object_index, object) in self.objects.iter().enumerate() {
let mut tiler = Tiler::from_outline(&object.outline,
object_index as u32,
&self.view_box,
&mut built_scene);
fn build_objects(&self) -> Vec<BuiltObject> {
// TODO(pcwalton): Parallelize!
self.objects.iter().enumerate().map(|(object_index, object)| {
let mut tiler = Tiler::new(&object.outline, object_index as u32, &self.view_box);
tiler.generate_tiles();
// TODO(pcwalton)
}
built_scene
tiler.built_object
}).collect()
}
fn push_svg_path(&mut self, value: &str, style: StyleId, name: String) {
@ -852,12 +847,9 @@ impl Segment {
}
#[inline(never)]
fn generate_fill_primitives(&self,
strip_origin: &Point2D<f32>,
primitives: &mut Vec<FillPrimitive>) {
if let Some(ref line_segment) = self.as_line_segment() {
//println!("generate_fill_primitives({:?}, {:?})", strip_origin, line_segment);
generate_fill_primitives_for_line(line_segment, strip_origin, primitives);
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);
return;
}
@ -866,55 +858,39 @@ impl Segment {
let flattener = Flattened::new(segment, FLATTENING_TOLERANCE);
let mut from = self.from;
for to in flattener {
generate_fill_primitives_for_line(&LineSegment { from, to }, strip_origin, primitives);
generate_fill_primitives_for_line(LineSegment { from, to }, built_object, tile_y);
from = to;
}
fn generate_fill_primitives_for_line(segment: &LineSegment<f32>,
strip_origin: &Point2D<f32>,
primitives: &mut Vec<FillPrimitive>) {
let mut segment = *segment;
// TODO(pcwalton): Factor this point-to-tile logic out. It keeps getting repeated…
let mut from_tile_index =
f32::max(0.0, f32::floor((segment.from.x - strip_origin.x) / TILE_WIDTH)) as u32;
loop {
let tile_offset =
Vector2D::new(from_tile_index as f32 * TILE_WIDTH + strip_origin.x,
strip_origin.y);
let to_tile_index =
f32::max(0.0, f32::floor((segment.to.x - strip_origin.x) / TILE_WIDTH)) as u32;
if from_tile_index == to_tile_index {
/*println!("... ... pushing LAST fill primitive {}: {:?} @ {:?}",
primitives.len(),
segment,
tile_offset);*/
primitives.push(FillPrimitive {
from: segment.from - tile_offset,
to: segment.to - tile_offset,
mask_tile_index: from_tile_index,
});
break;
}
// Split line at tile boundary.
let (next_tile_index, split_x) = if segment.from.x < segment.to.x {
(from_tile_index + 1, tile_offset.x + TILE_WIDTH)
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 {
(from_tile_index - 1, tile_offset.x)
(segment.to.x, segment.from.x)
};
let (prev_segment, next_segment) = segment.split_at_x(split_x);
primitives.push(FillPrimitive {
from: prev_segment.from - tile_offset,
to: prev_segment.to - tile_offset,
tile_index: from_tile_index,
});
let segment_tile_left = f32::floor(segment_left / TILE_WIDTH) as i16;
let segment_tile_right = f32::ceil(segment_right / TILE_WIDTH) as i16;
from_tile_index = next_tile_index;
segment = next_segment;
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;
}
}
built_object.add_fill(&fill_from, &fill_to, subsegment_tile_x, tile_y);
}
}
}
@ -948,39 +924,34 @@ bitflags! {
const TILE_WIDTH: f32 = 16.0;
const TILE_HEIGHT: f32 = 16.0;
struct Tiler<'o, 'p> {
struct Tiler<'o> {
outline: &'o Outline,
object_index: u32,
built_scene: &'p mut BuiltScene,
built_object: BuiltObject,
view_box: Rect<f32>,
bounds: Rect<f32>,
point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>,
strip_fills: Vec<FillPrimitive>,
strip_tiles: Vec<MaskTilePrimitive>,
used_strip_tiles: FixedBitSet,
old_active_edges: Vec<ActiveEdge>,
}
impl<'o, 'p> Tiler<'o, 'p> {
fn from_outline(outline: &'o Outline,
object_index: u32,
view_box: &Rect<f32>,
built_scene: &'p mut BuiltScene)
-> Tiler<'o, 'p> {
impl<'o> Tiler<'o> {
fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>) -> Tiler<'o> {
let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero());
let built_object = BuiltObject::new(&bounds);
Tiler {
outline,
object_index,
built_scene,
built_object,
view_box: *view_box,
bounds,
point_queue: SortedVector::new(),
active_edges: SortedVector::new(),
strip_fills: vec![],
strip_tiles: vec![],
used_strip_tiles: FixedBitSet::with_capacity(1),
old_active_edges: vec![],
}
}
@ -990,100 +961,43 @@ impl<'o, 'p> Tiler<'o, 'p> {
// Initialize the point queue.
self.init_point_queue();
// Clip to the view box.
let mut bounds = self.outline.bounds;
let max_x = f32::min(self.view_box.max_x(), bounds.max_x());
let max_y = f32::min(self.view_box.max_y(), bounds.max_y());
bounds.origin.x = f32::max(self.view_box.origin.x, bounds.origin.x);
bounds.size.width = f32::max(0.0, max_x - bounds.origin.x);
bounds.size.height = f32::max(0.0, max_y - bounds.origin.y);
// Reset active edges.
self.active_edges.clear();
let outline_tile_origin = Point2D::new(f32::floor(bounds.origin.x / TILE_WIDTH) as i16,
f32::floor(bounds.origin.y / TILE_HEIGHT) as i16);
let mut strip_origin = Point2D::new(outline_tile_origin.x as f32 * TILE_WIDTH,
outline_tile_origin.y as f32 * TILE_HEIGHT);
let strip_right_extent = f32::ceil(bounds.max_x() / TILE_WIDTH) * TILE_WIDTH;
let tiles_across = ((strip_right_extent - strip_origin.x) / TILE_WIDTH) as usize;
let mut tile_index_y = (f32::floor(self.view_box.origin.y / TILE_HEIGHT) * TILE_HEIGHT)
as i16;
self.strip_tiles.clear();
self.strip_tiles.reserve(tiles_across);
self.used_strip_tiles.grow(tiles_across);
self.old_active_edges.clear();
// Generate strips.
while strip_origin.y < bounds.max_y() {
// Determine strip bounds.
let strip_extent = Point2D::new(strip_right_extent, strip_origin.y + TILE_HEIGHT);
let strip_bounds = Rect::new(strip_origin,
Size2D::new(strip_right_extent - strip_origin.x,
strip_extent.y - strip_origin.y));
// Generate strip.
self.generate_strip(&strip_bounds, tile_index_y, tiles_across, &outline_tile_origin);
strip_origin.y = strip_extent.y;
tile_index_y += 1;
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);
}
}
#[inline(never)]
fn generate_strip(&mut self,
strip_bounds: &Rect<f32>,
tile_index_y: i16,
tiles_across: usize,
outline_tile_origin: &Point2D<i16>) {
// We can skip a bunch of steps if we're above the viewport.
let above_view_box = tile_index_y < 0;
// Reset strip info.
self.strip_fills.clear();
self.strip_tiles.clear();
self.used_strip_tiles.clear();
// Allocate tiles.
for tile_index_x in 0..tiles_across {
let tile_x = outline_tile_origin.x + tile_index_x as i16;
let tile_y = outline_tile_origin.y + tile_index_y;
self.strip_tiles.push(MaskTilePrimitive::new(tile_x, tile_y, self.object_index));
}
fn generate_strip(&mut self, strip_origin_y: i16) {
// Process old active edges.
self.process_old_active_edges(strip_bounds, tile_index_y);
self.process_old_active_edges(strip_origin_y);
// Add new active edges.
loop {
match self.point_queue.peek() {
Some(queued_endpoint) if queued_endpoint.y < strip_bounds.max_y() => {}
Some(_) | None => break,
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
}
self.add_new_active_edge(strip_bounds, tile_index_y);
}
// Finalize tiles.
if !above_view_box {
// NB: This order must not be changed!
self.flush_fills();
self.flush_tiles();
self.add_new_active_edge(strip_origin_y);
}
}
#[inline(never)]
fn process_old_active_edges(&mut self, strip_bounds: &Rect<f32>, tile_index_y: i16) {
// We can skip a bunch of steps if we're above the viewport.
let above_view_box = tile_index_y < 0;
fn process_old_active_edges(&mut self, tile_y: i16) {
let mut current_tile_x = self.built_object.tile_rect.origin.x;
let mut current_subtile_x = 0.0;
let mut current_winding = 0;
let (mut tile_index_x, mut current_left) = (0, strip_bounds.origin.x);
let mut winding = 0;
debug_assert!(self.old_active_edges.is_empty());
mem::swap(&mut self.old_active_edges, &mut self.active_edges.array);
for mut active_edge in self.old_active_edges.drain(..) {
// Determine x-intercept and winding.
let (segment_x, edge_winding) =
if active_edge.segment.from.y < active_edge.segment.to.y {
(active_edge.segment.from.x, 1)
@ -1092,50 +1006,30 @@ impl<'o, 'p> Tiler<'o, 'p> {
};
// Move over to the correct tile, filling in as we go.
let mut tile_left = strip_bounds.origin.x + (tile_index_x as f32) * TILE_WIDTH;
while tile_index_x < self.strip_tiles.len() {
let tile_right = tile_left + TILE_WIDTH;
/*println!("filling tile_left={}, segment_x={} winding={}?",
tile_left,
segment_x,
winding);*/
if tile_right > segment_x {
break
}
let mut segment_tile_x = f32::floor(segment_x / TILE_WIDTH) as i16;
while current_tile_x < segment_tile_x {
//println!("... filling!");
self.strip_tiles[tile_index_x].backdrop = winding as f32;
current_left = tile_right;
tile_left = tile_right;
tile_index_x += 1;
self.built_object.get_tile_mut(current_tile_x, tile_y).backdrop = current_winding;
current_tile_x += 1;
current_subtile_x = 0.0;
}
// Do subtile fills.
if current_left < segment_x && tile_index_x < self.strip_tiles.len() {
let subtile_left = Point2D::new(current_left - tile_left, 0.0);
let subtile_right = Point2D::new(segment_x - tile_left, 0.0);
self.strip_fills.push(FillPrimitive {
from: if edge_winding < 0 { subtile_left } else { subtile_right },
to: if edge_winding < 0 { subtile_right } else { subtile_left },
tile_index: tile_index_x as u32,
});
self.used_strip_tiles.insert(tile_index_x);
current_left = segment_x;
// Do subtile fill, if necessary.
debug_assert!(current_tile_x < self.built_object.tile_rect.max_x());
let current_x = (current_tile_x as f32) * TILE_WIDTH + current_subtile_x;
if segment_x >= current_x {
let (left, right) = (Point2D::new(current_x, 0.0), Point2D::new(segment_x, 0.0));
self.built_object.add_fill(if edge_winding < 0 { &left } else { &right },
if edge_winding < 0 { &right } else { &left },
current_tile_x,
tile_y);
}
// Update winding.
winding += edge_winding;
current_winding += edge_winding;
// Process the edge.
let fills = if above_view_box { None } else { Some(&mut self.strip_fills) };
process_active_edge(&mut active_edge.segment,
&strip_bounds,
fills,
&mut self.used_strip_tiles);
process_active_edge(&mut active_edge.segment, &mut self.built_object, tile_y);
if !active_edge.segment.is_none() {
self.active_edges.push(active_edge);
}
@ -1143,10 +1037,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
}
#[inline(never)]
fn add_new_active_edge(&mut self, strip_bounds: &Rect<f32>, tile_index_y: i16) {
// We can skip a bunch of steps if we're above the viewport.
let above_view_box = tile_index_y < 0;
fn add_new_active_edge(&mut self, tile_y: i16) {
let outline = &self.outline;
let point_index = self.point_queue.pop().unwrap().point_index;
@ -1156,13 +1047,11 @@ impl<'o, 'p> Tiler<'o, 'p> {
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) {
let fills = if above_view_box { None } else { Some(&mut self.strip_fills) };
process_active_segment(contour,
prev_endpoint_index,
&mut self.active_edges,
&strip_bounds,
fills,
&mut self.used_strip_tiles);
&mut self.built_object,
tile_y);
self.point_queue.push(QueuedEndpoint {
point_index: PointIndex::new(point_index.contour(), prev_endpoint_index),
@ -1171,13 +1060,11 @@ impl<'o, 'p> Tiler<'o, 'p> {
}
if contour.point_is_logically_above(point_index.point(), next_endpoint_index) {
let fills = if above_view_box { None } else { Some(&mut self.strip_fills) };
process_active_segment(contour,
point_index.point(),
&mut self.active_edges,
&strip_bounds,
fills,
&mut self.used_strip_tiles);
&mut self.built_object,
tile_y);
self.point_queue.push(QueuedEndpoint {
point_index: PointIndex::new(point_index.contour(), next_endpoint_index),
@ -1186,36 +1073,6 @@ impl<'o, 'p> Tiler<'o, 'p> {
}
}
#[inline(never)]
fn flush_tiles(&mut self) {
// Flush tiles.
for (tile_index_x, tile) in self.strip_tiles.iter().enumerate() {
if self.used_strip_tiles.contains(tile_index_x) {
self.built_scene.mask_tiles.push(*tile);
} else if tile.backdrop != 0.0 {
self.built_scene.solid_tiles.push(SolidTilePrimitive {
tile_x: tile.tile_x,
tile_y: tile.tile_y,
object_index: tile.object_index,
});
}
}
}
#[inline(never)]
fn flush_fills(&mut self) {
let first_tile_index = self.built_scene.mask_tiles.len() as u32;
for fill in &self.strip_fills {
let real_tile_index = first_tile_index +
self.used_strip_tiles.count_ones(0..(fill.tile_index as usize)) as u32;
self.built_scene.fills.push(FillPrimitive {
from: fill.from,
to: fill.to,
tile_index: real_tile_index,
});
}
}
#[inline(never)]
fn init_point_queue(&mut self) {
// Find MIN points.
@ -1245,202 +1102,170 @@ impl<'o, 'p> Tiler<'o, 'p> {
fn process_active_segment(contour: &Contour,
from_endpoint_index: u32,
active_edges: &mut SortedVector<ActiveEdge>,
strip_bounds: &Rect<f32>,
fills: Option<&mut Vec<FillPrimitive>>,
used_tiles: &mut FixedBitSet) {
let segment = contour.segment_after(from_endpoint_index);
built_object: &mut BuiltObject,
tile_y: i16) {
let mut segment = contour.segment_after(from_endpoint_index);
if segment.is_degenerate() {
return
}
let strip_range = (strip_bounds.origin.x)..(strip_bounds.max_x());
let mut segment = match segment.clip_x(strip_range.clone()) {
Some(segment) => segment,
None => return,
};
process_active_edge(&mut segment, &strip_bounds, fills, used_tiles);
process_active_edge(&mut segment, built_object, tile_y);
if !segment.is_none() {
active_edges.push(ActiveEdge::new(segment));
}
}
fn process_active_edge(active_edge: &mut Segment,
strip_bounds: &Rect<f32>,
mut fills: Option<&mut Vec<FillPrimitive>>,
used_tiles: &mut FixedBitSet) {
let strip_extent = strip_bounds.bottom_right();
fn process_active_edge(active_edge: &mut Segment, built_object: &mut BuiltObject, tile_y: i16) {
// Chop the segment.
// TODO(pcwalton): Maybe these shouldn't be Options?
let (upper_segment, lower_segment) = active_edge.split_y(strip_extent.y);
*active_edge = Segment::new();
let (upper_segment, lower_segment) = active_edge.split_y((tile_y + 1) as f32 * TILE_HEIGHT);
// Add fill primitives for upper part.
if let Some(segment) = upper_segment {
if let Some(ref mut fills) = fills {
segment.generate_fill_primitives(&strip_bounds.origin, *fills);
segment.generate_fill_primitives(built_object, tile_y);
}
// FIXME(pcwalton): Assumes x-monotonicity!
// FIXME(pcwalton): Don't hardcode a view box left of 0!
let mut min_x = f32::min(segment.from.x, segment.to.x);
let mut max_x = f32::max(segment.from.x, segment.to.x);
min_x = clamp(min_x, 0.0, strip_extent.x);
max_x = clamp(max_x, 0.0, strip_extent.x);
let tile_left = f32::floor(min_x / TILE_WIDTH) * TILE_WIDTH;
let tile_right = f32::ceil(max_x / TILE_WIDTH) * TILE_WIDTH;
let left_tile_index = (tile_left - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
let right_tile_index = (tile_right - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
// Set used bits.
for tile_index in left_tile_index..right_tile_index {
used_tiles.insert(tile_index as usize);
}
// Queue lower part.
*active_edge = lower_segment.unwrap_or(Segment::new());
}
match lower_segment {
Some(segment) => *active_edge = segment,
None => *active_edge = Segment::new(),
}
}
// Scene construction
// Culling
impl BuiltScene {
fn new(view_box: &Rect<f32>, object_count: u32) -> BuiltScene {
BuiltScene {
view_box: *view_box,
object_count,
fills: vec![],
solid_tiles: vec![],
mask_tiles: vec![],
tile_rect: round_rect_out_to_tile_bounds(view_box),
}
}
#[inline(never)]
fn merge_and_cull_subscenes(subscenes: &[&BuiltScene]) -> BuiltScene {
let mut scene = BuiltScene::new(Rect::zero(), 0);
if subscenes.is_empty() {
return scene;
}
fn from_objects(view_box: &Rect<f32>, objects: &[BuiltObject]) -> BuiltScene {
let mut scene = BuiltScene::new(view_box, objects.len() as u32);
let view_box = subscenes[0].view_box;
scene.view_box = view_box;
let mut z_buffer = FixedBitSet::with_capacity(scene.tile_rect.size.width as usize *
scene.tile_rect.size.height as usize);
let scene_tile_origin = Point2D::new(f32::floor(view_box.origin.x / TILE_WIDTH) as i32,
f32::floor(view_box.origin.y / TILE_HEIGHT) as i32);
let scene_tile_lower_right = Point2D::new(f32::ceil(view_box.max_x() / TILE_WIDTH) as i32,
f32::ceil(view_box.max_y() / TILE_HEIGHT) as i32);
let scene_tile_size = Size2D::new(scene_tile_lower_right.x - scene_tile_origin.x,
scene_tile_lower_right.y - scene_tile_origin.y).to_u32();
let mut object_tile_index_to_scene_mask_tile_index = vec![];
let mut z_buffer = FixedBitSet::with_capacity(scene_tile_size.width as usize *
scene_tile_size.height as usize);
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());
let mut fill_next_indices: Vec<usize> =
subscenes.iter().map(|subscene| subscene.fills.len()).collect();
let mut mask_tile_next_indices: Vec<usize> =
subscenes.iter().map(|subscene| subscene.mask_tiles.len()).collect();
let mut solid_tile_next_indices: Vec<usize> =
subscenes.iter().map(|subscene| subscene.solid_tiles.len()).collect();
for object_index in (0..scene.path_count).rev() {
// Find the subscene the object index belongs to.
let mut subscene_index = (0..subscenes.len()).filter(|subscene_index| {
let mask_tile_index = mask_tile_next_indices[subscene_index];
let subscene = &subscenes[subscene_index];
mask_tile_index > 0 &&
subscene.mask_tiles[mask_tile_index - 1].object_index == object_index
}).next();
if subscene_index.is_none() {
subscene_index = (0..subscenes.len()).filter(|subscene_index| {
let solid_tile_index = solid_tile_next_indices[subscene_index];
let subscene = &subscenes[subscene_index];
solid_tile_index > 0 &&
subscene.solid_tiles[solid_tile_index - 1].object_index == object_index
}).next();
}
// Look up that subscene.
let subscene_index = subscene_index.unwrap();
let subscene = &subscenes[subscene_index];
let mut fill_next_index = fill_next_indices[subscene_index];
let mut mask_tile_next_index = mask_tile_next_indices[subscene_index];
let mut solid_tile_next_index = solid_tile_next_indices[subscene_index];
let first_mask_tile_index = scene.mask_tiles.len();
// Copy mask tiles, culling as appropriate.
while mask_tile_next_index > 0 &&
subscene.mask_tiles[mask_tile_next_index - 1].object_index == object_index {
mask_tile_next_index -= 1;
let mut tile = subscene.mask_tiles[mask_tile_next_index];
let index = tile.tile_y as usize * scene_tile_size.width as usize +
tile.tile_x as usize;
let occluded = z_buffer[index];
if occluded {
tile.object_index = u32::MAX;
}
scene.mask_tiles.push(tile);
}
// Copy unoccluded solid tiles, updating the Z-buffer as necessary.
while solid_tile_next_index > 0 &&
subscene.solid_tiles[solid_tile_next_index - 1].object_index == object_index {
solid_tile_next_index -= 1;
let tile = subscene.solid_tiles[solid_tile_next_index];
let index = tile.tile_y as usize * scene_tile_size.width as usize +
tile.tile_x as usize;
if z_buffer[index] {
// 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.
break;
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);
}
z_buffer.insert(index);
scene.solid_tiles.push(tile);
}
// Copy unoccluded fill primitives.
while fill_next_index > 0 &&
subscene.fills[fill_next_index - 1].mask_tile_index >= mask_tile_next_index {
fill_next_index -= 1;
let mut fill = subscene.fills[fill_next_index];
fill.mask_tile_index = fill.mask_tile_index - mask_tile_next_index +
first_mask_tile_index;
if scene.mask_tiles[fill.mask_tile_index].object_index < u32::MAX {
scene.fills.push(fill);
}
}
// Update indices.
fill_next_indices[subscene_index] = fill_next_index;
mask_tile_next_indices[subscene_index] = mask_tile_next_index;
solid_tile_next_indices[subscene_index] = solid_tile_next_index;
// 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,
})
}
}
}
}
scene
}
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
}
}
// Primitives
#[derive(Debug)]
struct BuiltObject {
bounds: Rect<f32>,
tile_rect: Rect<i16>,
tiles: Vec<TileObjectPrimitive>,
fills: Vec<FillObjectPrimitive>,
mask_tiles: FixedBitSet,
}
#[derive(Debug)]
struct BuiltScene {
path_count: u32,
view_box: Rect<f32>,
fills: Vec<FillPrimitive>,
solid_tiles: Vec<SolidTilePrimitive>,
mask_tiles: Vec<MaskTilePrimitive>,
fills: Vec<FillScenePrimitive>,
solid_tiles: Vec<SolidTileScenePrimitive>,
mask_tiles: Vec<MaskTileScenePrimitive>,
object_count: u32,
tile_rect: Rect<i16>,
}
#[derive(Clone, Copy, Debug)]
struct FillPrimitive {
struct FillObjectPrimitive {
from: Point2D<f32>,
to: Point2D<f32>,
tile_x: i16,
tile_y: i16,
}
#[derive(Clone, Copy, Debug)]
struct TileObjectPrimitive {
tile_x: i16,
tile_y: i16,
backdrop: i32,
}
#[derive(Clone, Copy, Debug)]
struct FillScenePrimitive {
from: Point2D<f32>,
to: Point2D<f32>,
mask_tile_index: u32,
}
#[derive(Clone, Copy, Debug)]
struct SolidTilePrimitive {
struct SolidTileScenePrimitive {
tile_x: i16,
tile_y: i16,
object_index: u32,
}
#[derive(Clone, Copy, Debug)]
struct MaskTilePrimitive {
tile_x: i16,
tile_y: i16,
struct MaskTileScenePrimitive {
tile: TileObjectPrimitive,
object_index: u32,
backdrop: f32,
}
#[derive(Clone, Copy, Debug)]
@ -1451,24 +1276,63 @@ struct ColorU {
a: u8,
}
impl BuiltScene {
fn new(view_box: &Rect<f32>, path_count: u32) -> BuiltScene {
BuiltScene {
view_box: *view_box,
path_count,
fills: vec![],
solid_tiles: vec![],
mask_tiles: vec![],
// Utilities for built objects
impl BuiltObject {
fn new(bounds: &Rect<f32>) -> 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,
fills: vec![],
mask_tiles: FixedBitSet::with_capacity(tile_count),
}
}
fn add_fill(&mut self, from: &Point2D<f32>, to: &Point2D<f32>, tile_x: i16, tile_y: i16) {
let tile_index = self.tile_coords_to_index(tile_x, tile_y);
self.fills.push(FillObjectPrimitive { from: *from, to: *to, 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 {
fn write<W>(&self, writer: &mut W) -> io::Result<()> where W: Write {
writer.write_all(b"RIFF")?;
let header_size = 4 * 4;
let fill_size = self.fills.len() * mem::size_of::<FillPrimitive>();
let solid_tiles_size = self.solid_tiles.len() * mem::size_of::<SolidTilePrimitive>();
let mask_tiles_size = self.mask_tiles.len() * mem::size_of::<MaskTilePrimitive>();
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>();
writer.write_u32::<LittleEndian>((4 +
8 + header_size +
8 + fill_size +
@ -1489,7 +1353,7 @@ impl BuiltScene {
for fill_primitive in &self.fills {
write_point(writer, &fill_primitive.from)?;
write_point(writer, &fill_primitive.to)?;
writer.write_u32::<LittleEndian>(fill_primitive.tile_index)?;
writer.write_u32::<LittleEndian>(fill_primitive.mask_tile_index)?;
}
writer.write_all(b"soli")?;
@ -1503,9 +1367,9 @@ impl BuiltScene {
writer.write_all(b"mask")?;
writer.write_u32::<LittleEndian>(mask_tiles_size as u32)?;
for &tile_primitive in &self.mask_tiles {
writer.write_i16::<LittleEndian>(tile_primitive.tile_x)?;
writer.write_i16::<LittleEndian>(tile_primitive.tile_y)?;
writer.write_f32::<LittleEndian>(tile_primitive.backdrop)?;
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)?;
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
}
@ -1519,15 +1383,15 @@ impl BuiltScene {
}
}
impl SolidTilePrimitive {
fn new(tile_x: i16, tile_y: i16, object_index: u32) -> SolidTilePrimitive {
SolidTilePrimitive { tile_x, tile_y, object_index }
impl SolidTileScenePrimitive {
fn new(tile_x: i16, tile_y: i16, object_index: u32) -> SolidTileScenePrimitive {
SolidTileScenePrimitive { tile_x, tile_y, object_index }
}
}
impl MaskTilePrimitive {
fn new(tile_x: i16, tile_y: i16, object_index: u32) -> MaskTilePrimitive {
MaskTilePrimitive { tile_x, tile_y, backdrop: 0.0, object_index }
impl TileObjectPrimitive {
fn new(tile_x: i16, tile_y: i16) -> TileObjectPrimitive {
TileObjectPrimitive { tile_x, tile_y, backdrop: 0 }
}
}
@ -1541,6 +1405,17 @@ impl ColorU {
}
}
// 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)
}
// SVG stuff
struct SvgPathToPathEvents<'a, I> where I: Iterator<Item = SvgPathSegment> {