mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 07:48:02 +08:00
Improve rendering efficiency and add caching (#95)
Fixes #84 *Reduce heap allocations * Add caching for rendering svgs * Deduplicate UpdateCanvas Responses
This commit is contained in:
committed by
Keavon Chambers
parent
457c465342
commit
fc10575dfa
@@ -1,6 +1,8 @@
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Circle {
|
||||
shape: kurbo::Circle,
|
||||
@@ -17,13 +19,14 @@ impl Circle {
|
||||
}
|
||||
|
||||
impl LayerData for Circle {
|
||||
fn render(&self) -> String {
|
||||
format!(
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<circle cx="{}" cy="{}" r="{}" {} />"#,
|
||||
self.shape.center.x,
|
||||
self.shape.center.y,
|
||||
self.shape.radius,
|
||||
self.style.render(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ use crate::{DocumentError, LayerId};
|
||||
|
||||
use super::{Layer, LayerData, LayerDataTypes};
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Folder {
|
||||
next_assignment_id: LayerId,
|
||||
@@ -10,12 +12,10 @@ pub struct Folder {
|
||||
}
|
||||
|
||||
impl LayerData for Folder {
|
||||
fn render(&self) -> String {
|
||||
self.layers
|
||||
.iter()
|
||||
.filter(|layer| layer.visible)
|
||||
.map(|layer| layer.data.render())
|
||||
.fold(String::with_capacity(self.layers.len() * 30), |s, n| s + "\n" + &n)
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
self.layers.iter_mut().for_each(|layer| {
|
||||
let _ = writeln!(svg, "{}", layer.render());
|
||||
});
|
||||
}
|
||||
}
|
||||
impl Folder {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Line {
|
||||
shape: kurbo::Line,
|
||||
@@ -17,14 +19,15 @@ impl Line {
|
||||
}
|
||||
|
||||
impl LayerData for Line {
|
||||
fn render(&self) -> String {
|
||||
format!(
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<line x1="{}" y1="{}" x2="{}" y2="{}" {} />"#,
|
||||
self.shape.p0.x,
|
||||
self.shape.p0.y,
|
||||
self.shape.p1.x,
|
||||
self.shape.p1.y,
|
||||
self.style.render(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod folder;
|
||||
pub use folder::Folder;
|
||||
|
||||
pub trait LayerData {
|
||||
fn render(&self) -> String;
|
||||
fn render(&mut self, svg: &mut String);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -33,14 +33,14 @@ pub enum LayerDataTypes {
|
||||
}
|
||||
|
||||
impl LayerDataTypes {
|
||||
pub fn render(&self) -> String {
|
||||
pub fn render(&mut self, svg: &mut String) {
|
||||
match self {
|
||||
Self::Folder(f) => f.render(),
|
||||
Self::Circle(c) => c.render(),
|
||||
Self::Rect(r) => r.render(),
|
||||
Self::Line(l) => l.render(),
|
||||
Self::PolyLine(pl) => pl.render(),
|
||||
Self::Shape(s) => s.render(),
|
||||
Self::Folder(f) => f.render(svg),
|
||||
Self::Circle(c) => c.render(svg),
|
||||
Self::Rect(r) => r.render(svg),
|
||||
Self::Line(l) => l.render(svg),
|
||||
Self::PolyLine(pl) => pl.render(svg),
|
||||
Self::Shape(s) => s.render(svg),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,10 +50,30 @@ pub struct Layer {
|
||||
pub visible: bool,
|
||||
pub name: Option<String>,
|
||||
pub data: LayerDataTypes,
|
||||
pub cache: String,
|
||||
pub cache_dirty: bool,
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
pub fn new(data: LayerDataTypes) -> Self {
|
||||
Self { visible: true, name: None, data }
|
||||
Self {
|
||||
visible: true,
|
||||
name: None,
|
||||
data,
|
||||
cache: String::new(),
|
||||
cache_dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self) -> &str {
|
||||
if !self.visible {
|
||||
return "";
|
||||
}
|
||||
if self.cache_dirty {
|
||||
self.cache.clear();
|
||||
self.data.render(&mut self.cache);
|
||||
self.cache_dirty = false;
|
||||
}
|
||||
self.cache.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,24 +19,26 @@ impl PolyLine {
|
||||
}
|
||||
|
||||
impl LayerData for PolyLine {
|
||||
fn render(&self) -> String {
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
if self.points.is_empty() {
|
||||
return String::new();
|
||||
return;
|
||||
}
|
||||
let points = self.points.iter().fold(String::new(), |mut acc, p| {
|
||||
let _ = write!(&mut acc, " {:.3} {:.3}", p.x, p.y);
|
||||
acc
|
||||
let _ = write!(svg, r#"<polyline points=""#);
|
||||
self.points.iter().for_each(|p| {
|
||||
let _ = write!(svg, " {:.3} {:.3}", p.x, p.y);
|
||||
});
|
||||
format!(r#"<polyline points="{}" {}/>"#, &points[1..], self.style.render())
|
||||
let _ = write!(svg, r#"" {}/>"#, self.style.render());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn polyline_should_render() {
|
||||
let polyline = PolyLine {
|
||||
let mut polyline = PolyLine {
|
||||
points: vec![kurbo::Point::new(3.0, 4.12354), kurbo::Point::new(1.0, 5.54)],
|
||||
style: style::PathStyle::new(Some(style::Stroke::new(crate::color::Color::GREEN, 0.4)), None),
|
||||
};
|
||||
|
||||
assert_eq!(r#"<polyline points="3.000 4.124 1.000 5.540" style="stroke: #00FF00FF;stroke-width:0.4;"/>"#, polyline.render());
|
||||
let mut svg = String::new();
|
||||
polyline.render(&mut svg);
|
||||
assert_eq!(r#"<polyline points=" 3.000 4.124 1.000 5.540" style="stroke: #00FF00FF;stroke-width:0.4;"/>"#, svg);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Rect {
|
||||
shape: kurbo::Rect,
|
||||
@@ -17,14 +19,15 @@ impl Rect {
|
||||
}
|
||||
|
||||
impl LayerData for Rect {
|
||||
fn render(&self) -> String {
|
||||
format!(
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(
|
||||
svg,
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" {} />"#,
|
||||
self.shape.min_x(),
|
||||
self.shape.min_y(),
|
||||
self.shape.width(),
|
||||
self.shape.height(),
|
||||
self.style.render(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::shape_points;
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Shape {
|
||||
shape: shape_points::ShapePoints,
|
||||
@@ -19,7 +21,7 @@ impl Shape {
|
||||
}
|
||||
|
||||
impl LayerData for Shape {
|
||||
fn render(&self) -> String {
|
||||
format!(r#"<polygon points="{}" {} />"#, self.shape, self.style.render(),)
|
||||
fn render(&mut self, svg: &mut String) {
|
||||
let _ = write!(svg, r#"<polygon points="{}" {} />"#, self.shape, self.style.render(),);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user