mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 10:58:04 +08:00
Implement viewport culling (#667)
* culling is working * fixed tests * Ready for review * cleanup * code review * Fix import Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -42,10 +42,10 @@ impl Default for Document {
|
||||
|
||||
impl Document {
|
||||
/// Wrapper around render, that returns the whole document as a Response.
|
||||
pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache) -> String {
|
||||
pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> String {
|
||||
let mut svg_defs = String::from("<defs>");
|
||||
|
||||
self.root.render(&mut vec![], mode, &mut svg_defs, font_cache);
|
||||
self.root.render(&mut vec![], mode, &mut svg_defs, font_cache, culling_bounds);
|
||||
|
||||
svg_defs.push_str("</defs>");
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ pub struct FolderLayer {
|
||||
}
|
||||
|
||||
impl LayerData for FolderLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) {
|
||||
for layer in &mut self.layers {
|
||||
let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache));
|
||||
let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache, culling_bounds));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ pub struct ImageLayer {
|
||||
}
|
||||
|
||||
impl LayerData for ImageLayer {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache) {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
|
||||
let transform = self.transform(transforms, view_mode);
|
||||
let inverse = transform.inverse();
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ pub trait LayerData {
|
||||
/// let mut svg = String::new();
|
||||
///
|
||||
/// // Render the shape without any transforms, in normal view mode
|
||||
/// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default());
|
||||
/// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default(), None);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// svg,
|
||||
@@ -70,7 +70,7 @@ pub trait LayerData {
|
||||
/// </g>"
|
||||
/// );
|
||||
/// ```
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache);
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>);
|
||||
|
||||
/// Determine the layers within this layer that intersect a given quad.
|
||||
/// # Example
|
||||
@@ -117,8 +117,8 @@ pub trait LayerData {
|
||||
}
|
||||
|
||||
impl LayerData for LayerDataType {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
|
||||
self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache)
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, view_mode: ViewMode, font_cache: &FontCache, viewport_bounds: Option<[DVec2; 2]>) {
|
||||
self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache, viewport_bounds)
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
|
||||
@@ -223,16 +223,29 @@ impl Layer {
|
||||
LayerIter { stack: vec![self] }
|
||||
}
|
||||
|
||||
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache) -> &str {
|
||||
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> &str {
|
||||
if !self.visible {
|
||||
return "";
|
||||
}
|
||||
|
||||
transforms.push(self.transform);
|
||||
if let Some(viewport_bounds) = culling_bounds {
|
||||
if let Some(bounding_box) = self.data.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), font_cache) {
|
||||
let is_overlapping =
|
||||
viewport_bounds[0].x < bounding_box[1].x && bounding_box[0].x < viewport_bounds[1].x && viewport_bounds[0].y < bounding_box[1].y && bounding_box[0].y < viewport_bounds[1].y;
|
||||
if !is_overlapping {
|
||||
transforms.pop();
|
||||
self.cache.clear();
|
||||
self.cache_dirty = true;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.cache_dirty {
|
||||
transforms.push(self.transform);
|
||||
self.thumbnail_cache.clear();
|
||||
self.svg_defs_cache.clear();
|
||||
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache);
|
||||
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache, culling_bounds);
|
||||
|
||||
self.cache.clear();
|
||||
let _ = writeln!(self.cache, r#"<g transform="matrix("#);
|
||||
@@ -246,9 +259,10 @@ impl Layer {
|
||||
self.opacity,
|
||||
self.thumbnail_cache.as_str()
|
||||
);
|
||||
transforms.pop();
|
||||
|
||||
self.cache_dirty = false;
|
||||
}
|
||||
transforms.pop();
|
||||
svg_defs.push_str(&self.svg_defs_cache);
|
||||
|
||||
self.cache.as_str()
|
||||
|
||||
@@ -25,13 +25,14 @@ pub struct ShapeLayer {
|
||||
pub path: BezPath,
|
||||
/// The visual style of the shape.
|
||||
pub style: style::PathStyle,
|
||||
// TODO: We might be able to remove this in a future refactor
|
||||
pub render_index: i32,
|
||||
/// Whether or not the [path](ShapeLayer::path) connects to itself.
|
||||
pub closed: bool,
|
||||
}
|
||||
|
||||
impl LayerData for ShapeLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
|
||||
let mut path = self.path.clone();
|
||||
|
||||
let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box();
|
||||
@@ -89,7 +90,7 @@ impl ShapeLayer {
|
||||
(_, -1) => 0,
|
||||
(_, x) => (transforms.len() as i32 - x).max(0) as usize,
|
||||
};
|
||||
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
|
||||
transforms.iter().skip(start).fold(DAffine2::IDENTITY, |a, b| a * *b)
|
||||
}
|
||||
|
||||
pub fn from_bez_path(bez_path: BezPath, style: PathStyle, closed: bool) -> Self {
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct TextLayer {
|
||||
}
|
||||
|
||||
impl LayerData for TextLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, font_cache: &FontCache) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, view_mode: ViewMode, font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) {
|
||||
let transform = self.transform(transforms, view_mode);
|
||||
let inverse = transform.inverse();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user