Auto merge of #419 - pcwalton:tile-iteration-cap, r=pcwalton

Clip line segments before tiling them, and remove the cap on the number of iterations when tiling.

Closes #416.
This commit is contained in:
bors-servo 2020-07-28 15:58:36 -04:00 committed by GitHub
commit 37a328552e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 10 deletions

View File

@ -18,7 +18,7 @@ use arrayvec::ArrayVec;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::util::lerp; use pathfinder_geometry::util::lerp;
use pathfinder_geometry::vector::{Vector2F, Vector4F}; use pathfinder_geometry::vector::{Vector2F, Vector4F, vec2f};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::Debug; use std::fmt::Debug;
use std::mem; use std::mem;
@ -490,6 +490,80 @@ pub(crate) fn rect_is_inside_polygon(rect: RectF, polygon_points: &[Vector2F]) -
true true
} }
/// Clips a line segment to an axis-aligned rectangle using Cohen-Sutherland clipping.
pub fn clip_line_segment_to_rect(mut line_segment: LineSegment2F, rect: RectF)
-> Option<LineSegment2F> {
let mut outcode_from = compute_outcode(line_segment.from(), rect);
let mut outcode_to = compute_outcode(line_segment.to(), rect);
loop {
if outcode_from.is_empty() && outcode_to.is_empty() {
return Some(line_segment);
}
if !(outcode_from & outcode_to).is_empty() {
return None;
}
let clip_from = outcode_from.bits() > outcode_to.bits();
let (mut point, outcode) = if clip_from {
(line_segment.from(), outcode_from)
} else {
(line_segment.to(), outcode_to)
};
if outcode.contains(Outcode::LEFT) {
point = vec2f(rect.min_x(),
lerp(line_segment.from_y(),
line_segment.to_y(),
(line_segment.min_x() - line_segment.from_x()) /
(line_segment.max_x() - line_segment.min_x())));
} else if outcode.contains(Outcode::RIGHT) {
point = vec2f(rect.max_x(),
lerp(line_segment.from_y(),
line_segment.to_y(),
(line_segment.max_x() - line_segment.from_x()) /
(line_segment.max_x() - line_segment.min_x())));
} else if outcode.contains(Outcode::TOP) {
point = vec2f(lerp(line_segment.from_x(),
line_segment.to_x(),
(line_segment.min_y() - line_segment.from_y()) /
(line_segment.max_y() - line_segment.min_y())),
rect.min_y());
} else if outcode.contains(Outcode::LEFT) {
point = vec2f(lerp(line_segment.from_x(),
line_segment.to_x(),
(line_segment.max_y() - line_segment.from_y()) /
(line_segment.max_y() - line_segment.min_y())),
rect.min_y());
}
if clip_from {
line_segment.set_from(point);
outcode_from = compute_outcode(point, rect);
} else {
line_segment.set_to(point);
outcode_to = compute_outcode(point, rect);
}
}
fn compute_outcode(point: Vector2F, rect: RectF) -> Outcode {
let mut outcode = Outcode::empty();
if point.x() < rect.min_x() {
outcode.insert(Outcode::LEFT);
}
if point.y() < rect.min_y() {
outcode.insert(Outcode::TOP);
}
if point.x() > rect.max_x() {
outcode.insert(Outcode::RIGHT);
}
if point.y() > rect.max_y() {
outcode.insert(Outcode::BOTTOM);
}
outcode
}
}
bitflags! { bitflags! {
struct Outcode: u8 { struct Outcode: u8 {
const LEFT = 0x01; const LEFT = 0x01;

View File

@ -50,7 +50,7 @@ const CURVE_IS_CUBIC: u32 = 0x40000000;
const MAX_CLIP_BATCHES: u32 = 32; const MAX_CLIP_BATCHES: u32 = 32;
pub(crate) struct SceneBuilder<'a, 'b, 'c, 'd> { pub(crate) struct SceneBuilder<'a, 'b, 'c, 'd> {
scene: &'a mut Scene, pub(crate) scene: &'a mut Scene,
built_options: &'b PreparedBuildOptions, built_options: &'b PreparedBuildOptions,
next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT], next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT],
pub(crate) sink: &'c mut SceneSink<'d>, pub(crate) sink: &'c mut SceneSink<'d>,

View File

@ -17,6 +17,7 @@ use crate::gpu_data::AlphaTileId;
use crate::options::PrepareMode; use crate::options::PrepareMode;
use crate::scene::{ClipPathId, PathId}; use crate::scene::{ClipPathId, PathId};
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH, TilingPathInfo}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH, TilingPathInfo};
use pathfinder_content::clip;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{ContourIterFlags, Outline}; use pathfinder_content::outline::{ContourIterFlags, Outline};
use pathfinder_content::segment::Segment; use pathfinder_content::segment::Segment;
@ -24,6 +25,7 @@ use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_simd::default::{F32x2, U32x2}; use pathfinder_simd::default::{F32x2, U32x2};
use std::f32::NEG_INFINITY;
const FLATTENING_TOLERANCE: f32 = 0.25; const FLATTENING_TOLERANCE: f32 = 0.25;
@ -189,6 +191,14 @@ fn process_segment(segment: &Segment,
fn process_line_segment(line_segment: LineSegment2F, fn process_line_segment(line_segment: LineSegment2F,
scene_builder: &SceneBuilder, scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder) { object_builder: &mut ObjectBuilder) {
let view_box = scene_builder.scene.view_box();
let clip_box = RectF::from_points(vec2f(view_box.min_x(), NEG_INFINITY),
view_box.lower_right());
let line_segment = match clip::clip_line_segment_to_rect(line_segment, clip_box) {
None => return,
Some(line_segment) => line_segment,
};
let tile_size = vec2f(TILE_WIDTH as f32, TILE_HEIGHT as f32); let tile_size = vec2f(TILE_WIDTH as f32, TILE_HEIGHT as f32);
let tile_size_recip = Vector2F::splat(1.0) / tile_size; let tile_size_recip = Vector2F::splat(1.0) / tile_size;
@ -214,12 +224,8 @@ fn process_line_segment(line_segment: LineSegment2F,
let (mut current_position, mut tile_coords) = (line_segment.from(), from_tile_coords); let (mut current_position, mut tile_coords) = (line_segment.from(), from_tile_coords);
let mut last_step_direction = None; let mut last_step_direction = None;
let mut iteration = 0;
loop { loop {
// Quick check to catch missing the end tile.
debug_assert!(iteration < MAX_ITERATIONS);
let next_step_direction = if t_max.x() < t_max.y() { let next_step_direction = if t_max.x() < t_max.y() {
StepDirection::X StepDirection::X
} else if t_max.x() > t_max.y() { } else if t_max.x() > t_max.y() {
@ -292,11 +298,7 @@ fn process_line_segment(line_segment: LineSegment2F,
current_position = next_position; current_position = next_position;
last_step_direction = next_step_direction; last_step_direction = next_step_direction;
iteration += 1;
} }
const MAX_ITERATIONS: u32 = 1024;
} }
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]