Refactor font_cache into render_data; delete image layer type

This commit is contained in:
Keavon Chambers
2023-01-29 20:31:14 -08:00
parent b47ec1b356
commit 8a9b9e7c3c
41 changed files with 395 additions and 554 deletions
+5 -6
View File
@@ -1,7 +1,6 @@
use super::layer_info::{Layer, LayerData, LayerDataType};
use super::style::RenderData;
use crate::intersection::Quad;
use crate::layers::text_layer::FontCache;
use crate::{DocumentError, LayerId};
use glam::DVec2;
@@ -21,7 +20,7 @@ pub struct FolderLayer {
}
impl LayerData for FolderLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool {
let mut any_child_requires_redraw = false;
for layer in &mut self.layers {
let (svg_value, requires_redraw) = layer.render(transforms, svg_defs, render_data);
@@ -31,18 +30,18 @@ impl LayerData for FolderLayer {
any_child_requires_redraw
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
for (layer, layer_id) in self.layers().iter().zip(&self.layer_ids) {
path.push(*layer_id);
layer.intersects_quad(quad, path, intersections, font_cache);
layer.intersects_quad(quad, path, intersections, render_data);
path.pop();
}
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.layers
.iter()
.filter_map(|layer| layer.data.bounding_box(transform * layer.transform, font_cache))
.filter_map(|layer| layer.data.bounding_box(transform * layer.transform, render_data))
.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
}
}
-116
View File
@@ -1,116 +0,0 @@
use super::base64_serde;
use super::layer_info::LayerData;
use super::style::{RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use glam::{DAffine2, DMat2, DVec2};
use kurbo::{Affine, BezPath, Shape as KurboShape};
use serde::{Deserialize, Serialize};
use std::fmt::Write;
#[derive(Clone, PartialEq, Deserialize, Serialize, specta::Type)]
pub struct ImageLayer {
pub mime: String,
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")]
#[specta(type = String)]
pub image_data: std::sync::Arc<Vec<u8>>,
// TODO: Have the browser dispose of this blob URL when this is dropped (like when the layer is deleted)
#[serde(skip)]
pub blob_url: Option<String>,
#[serde(skip)]
pub dimensions: DVec2,
}
impl LayerData for ImageLayer {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse();
if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return false;
}
let _ = writeln!(svg, r#"<g transform="matrix("#);
inverse.to_cols_array().iter().enumerate().for_each(|(i, entry)| {
let _ = svg.write_str(&(entry.to_string() + if i == 5 { "" } else { "," }));
});
let _ = svg.write_str(r#")">"#);
let svg_transform = transform
.to_cols_array()
.iter()
.enumerate()
.map(|(i, entry)| entry.to_string() + if i == 5 { "" } else { "," })
.collect::<String>();
let _ = write!(
svg,
r#"<image width="{}" height="{}" transform="matrix({})" href="{}"/>"#,
self.dimensions.x,
self.dimensions.y,
svg_transform,
self.blob_url.as_ref().unwrap_or(&String::new())
);
let _ = svg.write_str("</g>");
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
let mut path = self.bounds();
if transform.matrix2 == DMat2::ZERO {
return None;
}
path.apply_affine(glam_to_kurbo(transform));
let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box();
Some([(x0, y0).into(), (x1, y1).into()])
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
if intersect_quad_bez_path(quad, &self.bounds(), true) {
intersections.push(path.clone());
}
}
}
impl ImageLayer {
pub fn new(mime: String, image_data: std::sync::Arc<Vec<u8>>) -> Self {
Self {
mime,
image_data,
blob_url: None,
dimensions: DVec2::ONE,
}
}
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
let start = match mode {
ViewMode::Outline => 0,
_ => (transforms.len() as i32 - 1).max(0) as usize,
};
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
}
fn bounds(&self) -> BezPath {
kurbo::Rect::from_origin_size(kurbo::Point::ZERO, kurbo::Size::new(self.dimensions.x, self.dimensions.y)).to_path(0.)
}
}
impl std::fmt::Debug for ImageLayer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageLayer")
.field("mime", &self.mime)
.field("image_data", &"...")
.field("blob_url", &self.blob_url)
.field("dimensions", &self.dimensions)
.finish()
}
}
fn glam_to_kurbo(transform: DAffine2) -> Affine {
Affine::new(transform.to_cols_array())
}
+36 -45
View File
@@ -1,12 +1,10 @@
use super::blend_mode::BlendMode;
use super::folder_layer::FolderLayer;
use super::image_layer::ImageLayer;
use super::nodegraph_layer::NodeGraphFrameLayer;
use super::shape_layer::ShapeLayer;
use super::style::{PathStyle, RenderData};
use super::text_layer::TextLayer;
use crate::intersection::Quad;
use crate::layers::text_layer::FontCache;
use crate::DocumentError;
use crate::LayerId;
@@ -26,8 +24,6 @@ pub enum LayerDataType {
Shape(ShapeLayer),
/// A layer that wraps a [TextLayer] struct.
Text(TextLayer),
/// A layer that wraps an [ImageLayer] struct.
Image(ImageLayer),
/// A layer that wraps an [NodeGraphFrameLayer] struct.
NodeGraphFrame(NodeGraphFrameLayer),
}
@@ -38,7 +34,6 @@ impl LayerDataType {
LayerDataType::Shape(s) => s,
LayerDataType::Folder(f) => f,
LayerDataType::Text(t) => t,
LayerDataType::Image(i) => i,
LayerDataType::NodeGraphFrame(n) => n,
}
}
@@ -48,7 +43,6 @@ impl LayerDataType {
LayerDataType::Shape(s) => s,
LayerDataType::Folder(f) => f,
LayerDataType::Text(t) => t,
LayerDataType::Image(i) => i,
LayerDataType::NodeGraphFrame(n) => n,
}
}
@@ -83,7 +77,6 @@ impl From<&LayerDataType> for LayerDataTypeDiscriminant {
Folder(_) => LayerDataTypeDiscriminant::Folder,
Shape(_) => LayerDataTypeDiscriminant::Shape,
Text(_) => LayerDataTypeDiscriminant::Text,
Image(_) => LayerDataTypeDiscriminant::Image,
NodeGraphFrame(_) => LayerDataTypeDiscriminant::NodeGraphFrame,
}
}
@@ -133,8 +126,8 @@ pub trait LayerData {
///
/// // Render the shape without any transforms, in normal view mode
/// # let font_cache = Default::default();
/// let render_data = RenderData::new(ViewMode::Normal, &font_cache, None);
/// shape.render(&mut svg, &mut String::new(), &mut vec![], render_data);
/// let render_data = RenderData::new(&font_cache, ViewMode::Normal, None);
/// shape.render(&mut svg, &mut String::new(), &mut vec![], &render_data);
///
/// assert_eq!(
/// svg,
@@ -143,13 +136,13 @@ pub trait LayerData {
/// </g>"
/// );
/// ```
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool;
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool;
/// Determine the layers within this layer that intersect a given quad.
/// # Example
/// ```
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, ViewMode};
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, ViewMode, RenderData};
/// # use graphite_document_legacy::layers::layer_info::LayerData;
/// # use graphite_document_legacy::intersection::Quad;
/// # use glam::f64::{DAffine2, DVec2};
@@ -162,18 +155,20 @@ pub trait LayerData {
/// let quad = Quad::from_box([DVec2::ZERO, DVec2::ONE]);
/// let mut intersections = vec![];
///
/// shape.intersects_quad(quad, &mut vec![shape_id], &mut intersections, &Default::default());
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// shape.intersects_quad(quad, &mut vec![shape_id], &mut intersections, &render_data);
///
/// assert_eq!(intersections, vec![vec![shape_id]]);
/// ```
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache);
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData);
// TODO: this doctest fails because 0 != 1e-32, maybe assert difference < epsilon?
/// Calculate the bounding box for the layer's contents after applying a given transform.
/// # Example
/// ```no_run
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle};
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, RenderData};
/// # use graphite_document_legacy::layers::layer_info::LayerData;
/// # use glam::f64::{DAffine2, DVec2};
/// # use std::collections::HashMap;
@@ -182,24 +177,26 @@ pub trait LayerData {
/// // Calculate the bounding box without applying any transformations.
/// // (The identity transform maps every vector to itself.)
/// let transform = DAffine2::IDENTITY;
/// let bounding_box = shape.bounding_box(transform, &Default::default());
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// let bounding_box = shape.bounding_box(transform, &render_data);
///
/// assert_eq!(bounding_box, Some([DVec2::ZERO, DVec2::ONE]));
/// ```
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]>;
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]>;
}
impl LayerData for LayerDataType {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool {
self.inner_mut().render(svg, svg_defs, transforms, render_data)
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
self.inner().intersects_quad(quad, path, intersections, font_cache)
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
self.inner().intersects_quad(quad, path, intersections, render_data)
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.inner().bounding_box(transform, font_cache)
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.inner().bounding_box(transform, render_data)
}
}
@@ -305,7 +302,7 @@ impl Layer {
}
/// Renders the layer, returning the result and if a redraw is required
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> (&str, bool) {
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: &RenderData) -> (&str, bool) {
if !self.visible {
return ("", false);
}
@@ -314,10 +311,7 @@ impl Layer {
// Skip rendering if outside the viewport bounds
if let Some(viewport_bounds) = render_data.culling_bounds {
if let Some(bounding_box) = self
.data
.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), render_data.font_cache)
{
if let Some(bounding_box) = self.data.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), render_data) {
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 {
@@ -363,13 +357,13 @@ impl Layer {
(self.cache.as_str(), requires_redraw)
}
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
if !self.visible {
return;
}
let transformed_quad = self.transform.inverse() * quad;
self.data.intersects_quad(transformed_quad, path, intersections, font_cache)
self.data.intersects_quad(transformed_quad, path, intersections, render_data)
}
/// Compute the bounding box of the layer after applying a transform to it.
@@ -378,7 +372,7 @@ impl Layer {
/// ```
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::layer_info::Layer;
/// # use graphite_document_legacy::layers::style::PathStyle;
/// # use graphite_document_legacy::layers::style::{PathStyle, RenderData};
/// # use glam::DVec2;
/// # use glam::f64::DAffine2;
/// # use std::collections::HashMap;
@@ -386,27 +380,30 @@ impl Layer {
/// let layer: Layer = ShapeLayer::rectangle(PathStyle::default()).into();
///
/// // Apply the Identity transform, which leaves the points unchanged
/// let transform = DAffine2::IDENTITY;
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// assert_eq!(
/// layer.aabb_for_transform(DAffine2::IDENTITY, &Default::default()),
/// layer.aabb_for_transform(transform, &render_data),
/// Some([DVec2::ZERO, DVec2::ONE]),
/// );
///
/// // Apply a transform that scales every point by a factor of two
/// let transform = DAffine2::from_scale(DVec2::ONE * 2.);
/// assert_eq!(
/// layer.aabb_for_transform(transform, &Default::default()),
/// layer.aabb_for_transform(transform, &render_data),
/// Some([DVec2::ZERO, DVec2::ONE * 2.]),
/// );
pub fn aabb_for_transform(&self, transform: DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.data.bounding_box(transform, font_cache)
pub fn aabb_for_transform(&self, transform: DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.data.bounding_box(transform, render_data)
}
pub fn aabb(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.aabb_for_transform(self.transform, font_cache)
pub fn aabb(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.aabb_for_transform(self.transform, render_data)
}
pub fn bounding_transform(&self, font_cache: &FontCache) -> DAffine2 {
let scale = match self.aabb_for_transform(DAffine2::IDENTITY, font_cache) {
pub fn bounding_transform(&self, render_data: &RenderData) -> DAffine2 {
let scale = match self.aabb_for_transform(DAffine2::IDENTITY, render_data) {
Some([a, b]) => {
let dimensions = b - a;
DAffine2::from_scale(dimensions)
@@ -417,8 +414,8 @@ impl Layer {
self.transform * scale
}
pub fn layerspace_pivot(&self, font_cache: &FontCache) -> DVec2 {
let [mut min, max] = self.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]);
pub fn layerspace_pivot(&self, render_data: &RenderData) -> DVec2 {
let [mut min, max] = self.aabb_for_transform(DAffine2::IDENTITY, render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
// If the layer bounds are 0 in either axis then set them to one (to avoid div 0)
if (max.x - min.x) < f64::EPSILON * 1000. {
@@ -560,12 +557,6 @@ impl From<TextLayer> for Layer {
}
}
impl From<ImageLayer> for Layer {
fn from(from: ImageLayer) -> Layer {
Layer::new(LayerDataType::Image(from), DAffine2::IDENTITY.to_cols_array())
}
}
impl<'a> IntoIterator for &'a Layer {
type Item = &'a Layer;
type IntoIter = LayerIter<'a>;
+1 -4
View File
@@ -5,8 +5,7 @@
//! * [Folder layers](folder_layer::FolderLayer), which encapsulate sub-layers
//! * [Shape layers](shape_layer::ShapeLayer), which contain generic SVG [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)s
//! * [Text layers](text_layer::TextLayer), which contain a description of laid out text
//! * [Image layers](image_layer::ImageLayer), which contain a bitmap image
//! * [Nodegraph layers](nodegraph_layer::NodegraphLayer), which contain a node graph frame
//! * [Node Graph layers](nodegraph_layer::NodegraphLayer), which contain a node graph frame
//!
//! Refer to the module-level documentation for detailed information on each layer.
//!
@@ -20,8 +19,6 @@ pub mod base64_serde;
pub mod blend_mode;
/// Contains the [FolderLayer](folder_layer::FolderLayer) type that encapsulates other layers, including more folders.
pub mod folder_layer;
/// Contains the [ImageLayer](image_layer::ImageLayer) type that contains a bitmap image.
pub mod image_layer;
/// Contains the base [Layer](layer_info::Layer) type, an abstraction over the different types of layers.
pub mod layer_info;
/// Contains the [NodegraphLayer](nodegraph_layer::NodegraphLayer) type that contains a node graph.
@@ -2,7 +2,6 @@ use super::base64_serde;
use super::layer_info::LayerData;
use super::style::{RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use glam::{DAffine2, DMat2, DVec2};
@@ -34,7 +33,7 @@ pub struct ImageData {
}
impl LayerData for NodeGraphFrameLayer {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse();
@@ -81,7 +80,7 @@ impl LayerData for NodeGraphFrameLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
let mut path = self.bounds();
if transform.matrix2 == DMat2::ZERO {
@@ -93,7 +92,7 @@ impl LayerData for NodeGraphFrameLayer {
Some([(x0, y0).into(), (x1, y1).into()])
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _render_data: &RenderData) {
if intersect_quad_bez_path(quad, &self.bounds(), true) {
intersections.push(path.clone());
}
+3 -4
View File
@@ -1,7 +1,6 @@
use super::layer_info::LayerData;
use super::style::{self, PathStyle, RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use graphene_std::vector::subpath::Subpath;
@@ -27,7 +26,7 @@ pub struct ShapeLayer {
}
impl LayerData for ShapeLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
let mut subpath = self.shape.clone();
let layer_bounds = subpath.bounding_box().unwrap_or_default();
@@ -58,7 +57,7 @@ impl LayerData for ShapeLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
let mut subpath = self.shape.clone();
if transform.matrix2 == DMat2::ZERO {
return None;
@@ -68,7 +67,7 @@ impl LayerData for ShapeLayer {
subpath.bounding_box()
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _render_data: &RenderData) {
let filled = self.style.fill().is_some() || self.shape.manipulator_groups().last().filter(|manipulator_group| manipulator_group.is_close()).is_some();
if intersect_quad_bez_path(quad, &(&self.shape).into(), filled) {
intersections.push(path.clone());
+3 -3
View File
@@ -35,16 +35,16 @@ pub enum ViewMode {
/// Contains metadata for rendering the document as an svg
#[derive(Debug, Clone, Copy)]
pub struct RenderData<'a> {
pub view_mode: ViewMode,
pub font_cache: &'a FontCache,
pub view_mode: ViewMode,
pub culling_bounds: Option<[DVec2; 2]>,
}
impl<'a> RenderData<'a> {
pub fn new(view_mode: ViewMode, font_cache: &'a FontCache, culling_bounds: Option<[DVec2; 2]>) -> Self {
pub fn new(font_cache: &'a FontCache, view_mode: ViewMode, culling_bounds: Option<[DVec2; 2]>) -> Self {
Self {
view_mode,
font_cache,
view_mode,
culling_bounds,
}
}
+14 -14
View File
@@ -34,7 +34,7 @@ pub struct TextLayer {
}
impl LayerData for TextLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse();
@@ -67,7 +67,7 @@ impl LayerData for TextLayer {
font.map(|_| r#" style="font-family: local-font;""#).unwrap_or_default()
);
} else {
let buzz_face = self.load_face(render_data.font_cache);
let buzz_face = self.load_face(render_data);
let mut path = self.to_subpath(buzz_face);
@@ -89,8 +89,8 @@ impl LayerData for TextLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
let buzz_face = Some(self.load_face(font_cache)?);
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
let buzz_face = Some(self.load_face(render_data)?);
if transform.matrix2 == DMat2::ZERO {
return None;
@@ -99,8 +99,8 @@ impl LayerData for TextLayer {
Some((transform * self.bounding_box(&self.text, buzz_face)).bounding_box())
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
let buzz_face = self.load_face(font_cache);
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
let buzz_face = self.load_face(render_data);
if intersect_quad_bez_path(quad, &self.bounding_box(&self.text, buzz_face).path(), true) {
intersections.push(path.clone());
@@ -109,8 +109,8 @@ impl LayerData for TextLayer {
}
impl TextLayer {
pub fn load_face<'a>(&self, font_cache: &'a FontCache) -> Option<Face<'a>> {
font_cache.get(&self.font).map(|data| rustybuzz::Face::from_slice(data, 0).expect("Loading font failed"))
pub fn load_face<'a>(&self, render_data: &'a RenderData) -> Option<Face<'a>> {
render_data.font_cache.get(&self.font).map(|data| rustybuzz::Face::from_slice(data, 0).expect("Loading font failed"))
}
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
@@ -121,7 +121,7 @@ impl TextLayer {
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
}
pub fn new(text: String, style: PathStyle, size: f64, font: Font, font_cache: &FontCache) -> Self {
pub fn new(text: String, style: PathStyle, size: f64, font: Font, render_data: &RenderData) -> Self {
let mut new = Self {
text,
path_style: style,
@@ -132,7 +132,7 @@ impl TextLayer {
cached_path: None,
};
new.cached_path = Some(new.generate_path(new.load_face(font_cache)));
new.cached_path = Some(new.generate_path(new.load_face(render_data)));
new
}
@@ -150,8 +150,8 @@ impl TextLayer {
/// Converts to a [Subpath], without populating the cache.
#[inline]
pub fn to_subpath_nonmut(&self, font_cache: &FontCache) -> Subpath {
let buzz_face = self.load_face(font_cache);
pub fn to_subpath_nonmut(&self, render_data: &RenderData) -> Subpath {
let buzz_face = self.load_face(render_data);
self.cached_path
.clone()
@@ -170,8 +170,8 @@ impl TextLayer {
Quad::from_box([DVec2::ZERO, far])
}
pub fn update_text(&mut self, text: String, font_cache: &FontCache) {
let buzz_face = self.load_face(font_cache);
pub fn update_text(&mut self, text: String, render_data: &RenderData) {
let buzz_face = self.load_face(render_data);
self.text = text;
self.cached_path = Some(self.generate_path(buzz_face));