Basic rect clipping
This commit is contained in:
parent
0097ffab19
commit
02928f295d
|
@ -47,7 +47,7 @@ const MASK_TILE_INSTANCE_SIZE: GLint = 8;
|
|||
const MASK_FRAMEBUFFER_WIDTH: u32 = TILE_WIDTH * 256;
|
||||
const MASK_FRAMEBUFFER_HEIGHT: u32 = TILE_HEIGHT * 256;
|
||||
|
||||
const MAIN_FRAMEBUFFER_WIDTH: u32 = 800;
|
||||
const MAIN_FRAMEBUFFER_WIDTH: u32 = 1067;
|
||||
const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800;
|
||||
|
||||
const FILL_COLORS_TEXTURE_WIDTH: u32 = 256;
|
||||
|
@ -81,11 +81,15 @@ fn main() {
|
|||
let mut renderer = Renderer::new(&Size2D::new(drawable_width, drawable_height));
|
||||
|
||||
let mut scale = 1.0;
|
||||
//let mut theta = 0.0;
|
||||
|
||||
while !exit {
|
||||
let mut scene = base_scene.clone();
|
||||
scene.transform(&Transform2DF32::from_scale(&Point2DF32::new(scale, scale)));
|
||||
scale -= 0.1;
|
||||
//scene.transform(&Transform2DF32::from_rotation(theta));
|
||||
scene.transform(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
|
||||
//theta += 0.01;
|
||||
//scale -= 0.0003;
|
||||
scale += 0.0001;
|
||||
|
||||
let built_scene = build_scene(&scene, &options);
|
||||
|
||||
|
@ -189,6 +193,7 @@ fn build_scene(scene: &Scene, options: &Options) -> BuiltScene {
|
|||
|
||||
let total_elapsed_time = elapsed_object_build_time + elapsed_scene_build_time;
|
||||
|
||||
/*
|
||||
println!(
|
||||
"{:.3}ms ({:.3}ms objects, {:.3}ms scene) elapsed",
|
||||
total_elapsed_time, elapsed_object_build_time, elapsed_scene_build_time
|
||||
|
@ -203,6 +208,7 @@ fn build_scene(scene: &Scene, options: &Options) -> BuiltScene {
|
|||
batch.mask_tiles.len()
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
built_scene
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::outline::{Contour, PointFlags};
|
||||
use crate::point::Point2DF32;
|
||||
use euclid::{Point2D, Rect, Vector3D};
|
||||
use lyon_path::PathEvent;
|
||||
use std::mem;
|
||||
|
@ -115,3 +117,66 @@ impl Edge {
|
|||
Point2D::new(intersection.x / intersection.z, intersection.y / intersection.z)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ContourRectClipper {
|
||||
clip_rect: Rect<f32>,
|
||||
contour: Contour,
|
||||
}
|
||||
|
||||
impl ContourRectClipper {
|
||||
#[inline]
|
||||
pub(crate) fn new(clip_rect: &Rect<f32>, contour: Contour) -> ContourRectClipper {
|
||||
ContourRectClipper { clip_rect: *clip_rect, contour }
|
||||
}
|
||||
|
||||
pub(crate) fn clip(mut self) -> Contour {
|
||||
if self.clip_rect.contains_rect(&self.contour.bounds()) {
|
||||
return self.contour
|
||||
}
|
||||
|
||||
self.clip_against(Edge::Left(self.clip_rect.origin.x));
|
||||
self.clip_against(Edge::Top(self.clip_rect.origin.y));
|
||||
self.clip_against(Edge::Right(self.clip_rect.max_x()));
|
||||
self.clip_against(Edge::Bottom(self.clip_rect.max_y()));
|
||||
self.contour
|
||||
}
|
||||
|
||||
fn clip_against(&mut self, edge: Edge) {
|
||||
let (mut from, mut first_point) = (Point2D::zero(), false);
|
||||
let input = mem::replace(&mut self.contour, Contour::new());
|
||||
for event in input.iter() {
|
||||
let to = match event {
|
||||
PathEvent::MoveTo(to) => {
|
||||
from = to;
|
||||
first_point = true;
|
||||
continue
|
||||
}
|
||||
PathEvent::Close => break,
|
||||
PathEvent::LineTo(to) |
|
||||
PathEvent::QuadraticTo(_, to) |
|
||||
PathEvent::CubicTo(_, _, to) => to,
|
||||
PathEvent::Arc(..) => panic!("Arcs unsupported!"),
|
||||
};
|
||||
|
||||
if edge.point_is_inside(&to) {
|
||||
if !edge.point_is_inside(&from) {
|
||||
//println!("clip: {:?} {:?}", from, to);
|
||||
add_line(&edge.line_intersection(&from, &to),
|
||||
&mut self.contour,
|
||||
&mut first_point);
|
||||
}
|
||||
add_line(&to, &mut self.contour, &mut first_point);
|
||||
} else if edge.point_is_inside(&from) {
|
||||
//println!("clip: {:?} {:?}", from, to);
|
||||
add_line(&edge.line_intersection(&from, &to), &mut self.contour, &mut first_point);
|
||||
}
|
||||
|
||||
from = to;
|
||||
}
|
||||
|
||||
fn add_line(to: &Point2D<f32>, output: &mut Contour, first_point: &mut bool) {
|
||||
output.push_point(Point2DF32::from_euclid(*to), PointFlags::empty());
|
||||
*first_point = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
|
||||
//! A compressed in-memory representation of paths.
|
||||
|
||||
use crate::clip::ContourRectClipper;
|
||||
use crate::point::Point2DF32;
|
||||
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
||||
use crate::transform::Transform2DF32;
|
||||
use euclid::{Point2D, Rect};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use lyon_path::PathEvent;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::mem;
|
||||
|
@ -28,6 +29,7 @@ pub struct Outline {
|
|||
pub struct Contour {
|
||||
points: Vec<Point2DF32>,
|
||||
flags: Vec<PointFlags>,
|
||||
bounds: Rect<f32>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
@ -53,7 +55,6 @@ impl Outline {
|
|||
{
|
||||
let mut outline = Outline::new();
|
||||
let mut current_contour = Contour::new();
|
||||
let mut bounding_points = None;
|
||||
|
||||
for segment in segments {
|
||||
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
|
||||
|
@ -62,18 +63,12 @@ impl Outline {
|
|||
.contours
|
||||
.push(mem::replace(&mut current_contour, Contour::new()));
|
||||
}
|
||||
current_contour.push_point(
|
||||
segment.baseline.from(),
|
||||
PointFlags::empty(),
|
||||
&mut bounding_points,
|
||||
);
|
||||
current_contour.push_point(segment.baseline.from(), PointFlags::empty());
|
||||
}
|
||||
|
||||
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
||||
if !current_contour.is_empty() {
|
||||
outline
|
||||
.contours
|
||||
.push(mem::replace(&mut current_contour, Contour::new()));
|
||||
outline.push_contour(mem::replace(&mut current_contour, Contour::new()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -83,34 +78,17 @@ impl Outline {
|
|||
}
|
||||
|
||||
if !segment.is_line() {
|
||||
current_contour.push_point(
|
||||
segment.ctrl.from(),
|
||||
PointFlags::CONTROL_POINT_0,
|
||||
&mut bounding_points,
|
||||
);
|
||||
current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0);
|
||||
if !segment.is_quadratic() {
|
||||
current_contour.push_point(
|
||||
segment.ctrl.to(),
|
||||
PointFlags::CONTROL_POINT_1,
|
||||
&mut bounding_points,
|
||||
);
|
||||
current_contour.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1);
|
||||
}
|
||||
}
|
||||
|
||||
current_contour.push_point(
|
||||
segment.baseline.to(),
|
||||
PointFlags::empty(),
|
||||
&mut bounding_points,
|
||||
);
|
||||
current_contour.push_point(segment.baseline.to(), PointFlags::empty());
|
||||
}
|
||||
|
||||
if !current_contour.is_empty() {
|
||||
outline.contours.push(current_contour)
|
||||
}
|
||||
|
||||
if let Some((upper_left, lower_right)) = bounding_points {
|
||||
outline.bounds =
|
||||
Rect::from_points([upper_left.as_euclid(), lower_right.as_euclid()].iter())
|
||||
outline.push_contour(current_contour);
|
||||
}
|
||||
|
||||
outline
|
||||
|
@ -124,24 +102,38 @@ impl Outline {
|
|||
#[inline]
|
||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||
self.contours.iter_mut().for_each(|contour| contour.transform(transform));
|
||||
self.bounds = transform.transform_rect(&self.bounds);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push_contour(&mut self, contour: Contour) {
|
||||
if self.contours.is_empty() {
|
||||
self.bounds = contour.bounds;
|
||||
} else {
|
||||
self.bounds = self.bounds.union(&contour.bounds);
|
||||
}
|
||||
self.contours.push(contour);
|
||||
}
|
||||
|
||||
pub fn clip_against_rect(&mut self, clip_rect: &Rect<f32>) {
|
||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||
let contour = ContourRectClipper::new(clip_rect, contour).clip();
|
||||
if !contour.is_empty() {
|
||||
self.push_contour(contour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Contour {
|
||||
#[inline]
|
||||
pub fn new() -> Contour {
|
||||
Contour {
|
||||
points: vec![],
|
||||
flags: vec![],
|
||||
}
|
||||
Contour { points: vec![], flags: vec![], bounds: Rect::zero() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> ContourIter {
|
||||
ContourIter {
|
||||
contour: self,
|
||||
index: 0,
|
||||
}
|
||||
ContourIter { contour: self, index: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -154,29 +146,24 @@ impl Contour {
|
|||
self.points.len() as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bounds(&self) -> &Rect<f32> {
|
||||
&self.bounds
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position_of(&self, index: u32) -> Point2DF32 {
|
||||
self.points[index as usize]
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Pack both min and max into a single SIMD register?
|
||||
// TODO(pcwalton): SIMD.
|
||||
#[inline]
|
||||
fn push_point(
|
||||
&mut self,
|
||||
point: Point2DF32,
|
||||
flags: PointFlags,
|
||||
bounding_points: &mut Option<(Point2DF32, Point2DF32)>,
|
||||
) {
|
||||
pub(crate) fn push_point(&mut self, point: Point2DF32, flags: PointFlags) {
|
||||
let first = self.is_empty();
|
||||
union_rect(&mut self.bounds, point, first);
|
||||
|
||||
self.points.push(point);
|
||||
self.flags.push(flags);
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -271,8 +258,9 @@ impl Contour {
|
|||
|
||||
#[inline]
|
||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||
for point in &mut self.points {
|
||||
*point = transform.transform_point(point)
|
||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||
*point = transform.transform_point(point);
|
||||
union_rect(&mut self.bounds, *point, point_index == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,3 +396,19 @@ impl<'a> Iterator for ContourIter<'a> {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn union_rect(bounds: &mut Rect<f32>, new_point: Point2DF32, first: bool) {
|
||||
if first {
|
||||
*bounds = Rect::new(new_point.as_euclid(), Size2D::zero());
|
||||
return;
|
||||
}
|
||||
|
||||
let (mut min_x, mut min_y) = (bounds.origin.x, bounds.origin.y);
|
||||
let (mut max_x, mut max_y) = (bounds.max_x(), bounds.max_y());
|
||||
min_x = min_x.min(new_point.x());
|
||||
min_y = min_y.min(new_point.y());
|
||||
max_x = max_x.max(new_point.x());
|
||||
max_y = max_y.max(new_point.y());
|
||||
*bounds = Rect::new(Point2D::new(min_x, min_y), Size2D::new(max_x - min_x, max_y - min_y));
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
use crate::point::Point2DF32;
|
||||
use crate::segment::Segment;
|
||||
use crate::simd::F32x4;
|
||||
use euclid::Transform2D;
|
||||
use euclid::{Point2D, Rect, Size2D, Transform2D};
|
||||
use lyon_path::PathEvent;
|
||||
|
||||
/// An affine transform, optimized with SIMD.
|
||||
|
@ -40,6 +40,15 @@ impl Transform2DF32 {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_rotation(theta: f32) -> Transform2DF32 {
|
||||
let (sin_theta, cos_theta) = (theta.sin(), theta.cos());
|
||||
Transform2DF32 {
|
||||
matrix: F32x4::new(cos_theta, -sin_theta, sin_theta, cos_theta),
|
||||
vector: Point2DF32::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32)
|
||||
-> Transform2DF32 {
|
||||
|
@ -55,6 +64,21 @@ impl Transform2DF32 {
|
|||
Point2DF32(x11x12y21y22 + x11x12y21y22.zwzw() + self.vector.0)
|
||||
}
|
||||
|
||||
// TODO(pcwalton): SIMD.
|
||||
#[inline]
|
||||
pub fn transform_rect(&self, rect: &Rect<f32>) -> Rect<f32> {
|
||||
let upper_left = self.transform_point(&Point2DF32::from_euclid(rect.origin));
|
||||
let upper_right = self.transform_point(&Point2DF32::from_euclid(rect.top_right()));
|
||||
let lower_left = self.transform_point(&Point2DF32::from_euclid(rect.bottom_left()));
|
||||
let lower_right = self.transform_point(&Point2DF32::from_euclid(rect.bottom_right()));
|
||||
let min_x = upper_left.x().min(upper_right.x()).min(lower_left.x()).min(lower_right.x());
|
||||
let min_y = upper_left.y().min(upper_right.y()).min(lower_left.y()).min(lower_right.y());
|
||||
let max_x = upper_left.x().max(upper_right.x()).max(lower_left.x()).max(lower_right.x());
|
||||
let max_y = upper_left.y().max(upper_right.y()).max(lower_left.y()).max(lower_right.y());
|
||||
let (width, height) = (max_x - min_x, max_y - min_y);
|
||||
Rect::new(Point2D::new(min_x, min_y), Size2D::new(width, height))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn post_mul(&self, other: &Transform2DF32) -> Transform2DF32 {
|
||||
let lhs = self.matrix.xzxz() * other.matrix.xxyy();
|
||||
|
|
|
@ -215,6 +215,8 @@ impl BuiltObject {
|
|||
let segment_tile_left = (f32::floor(segment_left) as i32 / TILE_WIDTH as i32) as i16;
|
||||
let segment_tile_right =
|
||||
util::alignup_i32(f32::ceil(segment_right) as i32, TILE_WIDTH as i32) as i16;
|
||||
/*println!("segment_tile_left={} segment_tile_right={} tile_rect={:?}",
|
||||
segment_tile_left, segment_tile_right, self.tile_rect);*/
|
||||
|
||||
for subsegment_tile_x in segment_tile_left..segment_tile_right {
|
||||
let (mut fill_from, mut fill_to) = (segment.from(), segment.to());
|
||||
|
|
|
@ -99,10 +99,20 @@ impl Scene {
|
|||
}
|
||||
|
||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||
// TODO(pcwalton): Transform bounds?
|
||||
for object in &mut self.objects {
|
||||
object.outline.transform(transform)
|
||||
let mut bounds = Rect::zero();
|
||||
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
||||
object.outline.transform(transform);
|
||||
object.outline.clip_against_rect(&self.view_box);
|
||||
|
||||
if object_index == 0 {
|
||||
bounds = *object.outline.bounds();
|
||||
} else {
|
||||
bounds = bounds.union(object.outline.bounds());
|
||||
}
|
||||
}
|
||||
|
||||
//println!("new bounds={:?}", bounds);
|
||||
self.bounds = bounds;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,4 +147,4 @@ impl PathObject {
|
|||
pub fn scene_tile_index(tile_x: i16, tile_y: i16, tile_rect: Rect<i16>) -> u32 {
|
||||
(tile_y - tile_rect.origin.y) as u32 * tile_rect.size.width as u32
|
||||
+ (tile_x - tile_rect.origin.x) as u32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
|||
debug_assert!(self.old_active_edges.is_empty());
|
||||
mem::swap(&mut self.old_active_edges, &mut self.active_edges.array);
|
||||
|
||||
// FIXME(pcwalton): Yuck.
|
||||
let mut last_segment_x = -9999.0;
|
||||
|
||||
let tile_top = (i32::from(tile_y) * TILE_HEIGHT as i32) as f32;
|
||||
|
@ -178,7 +179,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
|||
|
||||
// Do final subtile fill, if necessary.
|
||||
debug_assert_eq!(current_tile_x, segment_tile_x);
|
||||
debug_assert!(current_tile_x < self.built_object.tile_rect.max_x());
|
||||
debug_assert!(current_tile_x <= self.built_object.tile_rect.max_x());
|
||||
let segment_subtile_x =
|
||||
segment_x - (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32;
|
||||
if segment_subtile_x > current_subtile_x {
|
||||
|
|
Loading…
Reference in New Issue