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:
TrueDoctor
2021-05-02 21:21:39 +02:00
committed by Keavon Chambers
parent 457c465342
commit fc10575dfa
14 changed files with 123 additions and 73 deletions

View File

@@ -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(),
)
);
}
}

View File

@@ -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 {

View File

@@ -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(),
)
);
}
}

View File

@@ -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()
}
}

View File

@@ -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);
}

View File

@@ -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(),
)
);
}
}

View File

@@ -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(),);
}
}