Skip to content

Commit

Permalink
Auto merge of rust-windowing#419 - pcwalton:tile-iteration-cap, r=pcw…
Browse files Browse the repository at this point in the history
…alton

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

Closes rust-windowing#416.
  • Loading branch information
bors-servo authored Jul 28, 2020
2 parents 4c8699a + 441051a commit 37a3285
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 10 deletions.
76 changes: 75 additions & 1 deletion content/src/clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use arrayvec::ArrayVec;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::util::lerp;
use pathfinder_geometry::vector::{Vector2F, Vector4F};
use pathfinder_geometry::vector::{Vector2F, Vector4F, vec2f};
use smallvec::SmallVec;
use std::fmt::Debug;
use std::mem;
Expand Down Expand Up @@ -490,6 +490,80 @@ pub(crate) fn rect_is_inside_polygon(rect: RectF, polygon_points: &[Vector2F]) -
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! {
struct Outcode: u8 {
const LEFT = 0x01;
Expand Down
2 changes: 1 addition & 1 deletion renderer/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CURVE_IS_CUBIC: u32 = 0x40000000;
const MAX_CLIP_BATCHES: u32 = 32;

pub(crate) struct SceneBuilder<'a, 'b, 'c, 'd> {
scene: &'a mut Scene,
pub(crate) scene: &'a mut Scene,
built_options: &'b PreparedBuildOptions,
next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT],
pub(crate) sink: &'c mut SceneSink<'d>,
Expand Down
18 changes: 10 additions & 8 deletions renderer/src/tiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ use crate::gpu_data::AlphaTileId;
use crate::options::PrepareMode;
use crate::scene::{ClipPathId, PathId};
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH, TilingPathInfo};
use pathfinder_content::clip;
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{ContourIterFlags, Outline};
use pathfinder_content::segment::Segment;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_simd::default::{F32x2, U32x2};
use std::f32::NEG_INFINITY;

const FLATTENING_TOLERANCE: f32 = 0.25;

Expand Down Expand Up @@ -189,6 +191,14 @@ fn process_segment(segment: &Segment,
fn process_line_segment(line_segment: LineSegment2F,
scene_builder: &SceneBuilder,
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_recip = Vector2F::splat(1.0) / tile_size;

Expand All @@ -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 last_step_direction = None;
let mut iteration = 0;

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() {
StepDirection::X
} else if t_max.x() > t_max.y() {
Expand Down Expand Up @@ -292,11 +298,7 @@ fn process_line_segment(line_segment: LineSegment2F,

current_position = next_position;
last_step_direction = next_step_direction;

iteration += 1;
}

const MAX_ITERATIONS: u32 = 1024;
}

#[derive(Clone, Copy, PartialEq, Debug)]
Expand Down

0 comments on commit 37a3285

Please sign in to comment.