Clip line segments before tiling them.
This reduces pathological behavior from very large off-screen segments. Part of #416.
This commit is contained in:
parent
4c8699a5e2
commit
41ad372253
|
@ -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;
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue