mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 19:08:05 +08:00
Refactor font_cache into render_data; delete image layer type
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
use crate::boolean_ops::composite_boolean_operation;
|
||||
use crate::intersection::Quad;
|
||||
use crate::layers::folder_layer::FolderLayer;
|
||||
use crate::layers::image_layer::ImageLayer;
|
||||
use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant};
|
||||
use crate::layers::nodegraph_layer::NodeGraphFrameLayer;
|
||||
use crate::layers::shape_layer::ShapeLayer;
|
||||
use crate::layers::style::RenderData;
|
||||
use crate::layers::text_layer::{Font, FontCache, TextLayer};
|
||||
use crate::layers::text_layer::{Font, TextLayer};
|
||||
use crate::{DocumentError, DocumentResponse, Operation};
|
||||
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
@@ -49,7 +48,7 @@ impl Default for Document {
|
||||
|
||||
impl Document {
|
||||
/// Wrapper around render, that returns the whole document as a Response.
|
||||
pub fn render_root(&mut self, render_data: RenderData) -> String {
|
||||
pub fn render_root(&mut self, render_data: &RenderData) -> String {
|
||||
// Render and append to the defs section
|
||||
let mut svg_defs = String::from("<defs>");
|
||||
self.root.render(&mut vec![], &mut svg_defs, render_data);
|
||||
@@ -62,7 +61,7 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Renders everything below the given layer contained within its parent folder.
|
||||
pub fn render_layers_below(&mut self, below_layer_path: &[LayerId], render_data: RenderData) -> Option<String> {
|
||||
pub fn render_layers_below(&mut self, below_layer_path: &[LayerId], render_data: &RenderData) -> Option<String> {
|
||||
// Split the path into the layer ID and its parent folder
|
||||
let (layer_id_to_render_below, parent_folder_path) = below_layer_path.split_last()?;
|
||||
|
||||
@@ -89,7 +88,7 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Renders a layer and its children
|
||||
pub fn render_layer(&mut self, layer_path: &[LayerId], render_data: RenderData) -> Option<String> {
|
||||
pub fn render_layer(&mut self, layer_path: &[LayerId], render_data: &RenderData) -> Option<String> {
|
||||
// Note: it is bad practice to directly clone and modify the document structure, this is a temporary hack until this whole system is replaced by the node graph
|
||||
let mut temp_clone = self.layer_mut(layer_path).ok()?.clone();
|
||||
|
||||
@@ -109,14 +108,14 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`.
|
||||
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
|
||||
self.layer(path).unwrap().intersects_quad(quad, path, intersections, font_cache);
|
||||
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
|
||||
self.layer(path).unwrap().intersects_quad(quad, path, intersections, render_data);
|
||||
}
|
||||
|
||||
/// Checks whether each layer under the root path intersects with the provided `quad` and returns the paths to all intersecting layers.
|
||||
pub fn intersects_quad_root(&self, quad: Quad, font_cache: &FontCache) -> Vec<Vec<LayerId>> {
|
||||
pub fn intersects_quad_root(&self, quad: Quad, render_data: &RenderData) -> Vec<Vec<LayerId>> {
|
||||
let mut intersections = Vec::new();
|
||||
self.intersects_quad(quad, &mut vec![], &mut intersections, font_cache);
|
||||
self.intersects_quad(quad, &mut vec![], &mut intersections, render_data);
|
||||
intersections
|
||||
}
|
||||
|
||||
@@ -407,32 +406,32 @@ impl Document {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn viewport_bounding_box(&self, path: &[LayerId], font_cache: &FontCache) -> Result<Option<[DVec2; 2]>, DocumentError> {
|
||||
pub fn viewport_bounding_box(&self, path: &[LayerId], render_data: &RenderData) -> Result<Option<[DVec2; 2]>, DocumentError> {
|
||||
let layer = self.layer(path)?;
|
||||
let transform = self.multiply_transforms(path)?;
|
||||
Ok(layer.data.bounding_box(transform, font_cache))
|
||||
Ok(layer.data.bounding_box(transform, render_data))
|
||||
}
|
||||
|
||||
pub fn bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Result<Option<([DVec2; 2], DAffine2)>, DocumentError> {
|
||||
pub fn bounding_box_and_transform(&self, path: &[LayerId], render_data: &RenderData) -> Result<Option<([DVec2; 2], DAffine2)>, DocumentError> {
|
||||
let layer = self.layer(path)?;
|
||||
let transform = self.multiply_transforms(&path[..path.len() - 1])?;
|
||||
Ok(layer.data.bounding_box(layer.transform, font_cache).map(|bounds| (bounds, transform)))
|
||||
Ok(layer.data.bounding_box(layer.transform, render_data).map(|bounds| (bounds, transform)))
|
||||
}
|
||||
|
||||
/// Compute the center of transformation multiplied with `Document::multiply_transforms`.
|
||||
pub fn pivot(&self, path: &[LayerId], font_cache: &FontCache) -> Option<DVec2> {
|
||||
pub fn pivot(&self, path: &[LayerId], render_data: &RenderData) -> Option<DVec2> {
|
||||
let layer = self.layer(path).ok()?;
|
||||
Some(self.multiply_transforms(path).unwrap_or_default().transform_point2(layer.layerspace_pivot(font_cache)))
|
||||
Some(self.multiply_transforms(path).unwrap_or_default().transform_point2(layer.layerspace_pivot(render_data)))
|
||||
}
|
||||
|
||||
pub fn visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
pub fn visible_layers_bounding_box(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
|
||||
let mut paths = vec![];
|
||||
self.visible_layers(&mut vec![], &mut paths).ok()?;
|
||||
self.combined_viewport_bounding_box(paths.iter().map(|x| x.as_slice()), font_cache)
|
||||
self.combined_viewport_bounding_box(paths.iter().map(|x| x.as_slice()), render_data)
|
||||
}
|
||||
|
||||
pub fn combined_viewport_bounding_box<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
let boxes = paths.filter_map(|path| self.viewport_bounding_box(path, font_cache).ok()?);
|
||||
pub fn combined_viewport_bounding_box<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>, render_data: &RenderData) -> Option<[DVec2; 2]> {
|
||||
let boxes = paths.filter_map(|path| self.viewport_bounding_box(path, render_data).ok()?);
|
||||
boxes.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
|
||||
}
|
||||
|
||||
@@ -552,7 +551,7 @@ impl Document {
|
||||
|
||||
/// Mutate the document by applying the `operation` to it. If the operation necessitates a
|
||||
/// reaction from the frontend, responses may be returned.
|
||||
pub fn handle_operation(&mut self, operation: Operation, font_cache: &FontCache) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
|
||||
pub fn handle_operation(&mut self, operation: Operation, render_data: &RenderData) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
|
||||
use DocumentResponse::*;
|
||||
|
||||
operation.pseudo_hash().hash(&mut self.state_identifier);
|
||||
@@ -590,7 +589,7 @@ impl Document {
|
||||
font_style,
|
||||
} => {
|
||||
let font = Font::new(font_name, font_style);
|
||||
let layer_text = TextLayer::new(text, style, size, font, font_cache);
|
||||
let layer_text = TextLayer::new(text, style, size, font, render_data);
|
||||
let layer_data = LayerDataType::Text(layer_text);
|
||||
let layer = Layer::new(layer_data, transform);
|
||||
|
||||
@@ -598,20 +597,6 @@ impl Document {
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
|
||||
}
|
||||
Operation::AddImage {
|
||||
path,
|
||||
transform,
|
||||
insert_index,
|
||||
image_data,
|
||||
mime,
|
||||
} => {
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
let layer = Layer::new(LayerDataType::Image(ImageLayer::new(mime, image_data)), transform);
|
||||
|
||||
self.set_layer(&path, layer, insert_index)?;
|
||||
|
||||
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
|
||||
}
|
||||
Operation::AddNodeGraphFrame {
|
||||
path,
|
||||
insert_index,
|
||||
@@ -658,7 +643,7 @@ impl Document {
|
||||
.layer_mut(id)
|
||||
.ok_or_else(|| DocumentError::LayerNotFound(path.clone()))?
|
||||
.as_text_mut()?
|
||||
.update_text(new_text, font_cache);
|
||||
.update_text(new_text, render_data);
|
||||
|
||||
self.mark_as_dirty(&path)?;
|
||||
|
||||
@@ -808,7 +793,7 @@ impl Document {
|
||||
|
||||
text.font = Font::new(font_family, font_style);
|
||||
text.size = size;
|
||||
text.cached_path = Some(text.generate_path(text.load_face(font_cache)));
|
||||
text.cached_path = Some(text.generate_path(text.load_face(render_data)));
|
||||
self.mark_as_dirty(&path)?;
|
||||
Some([vec![DocumentChanged, LayerChanged { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
|
||||
}
|
||||
@@ -836,18 +821,14 @@ impl Document {
|
||||
Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat())
|
||||
}
|
||||
Operation::SetLayerBlobUrl { layer_path, blob_url, resolution } => {
|
||||
let layer = self.layer_mut(&layer_path).unwrap_or_else(|_| panic!("Blob url for invalid layer with path '{:?}'", layer_path));
|
||||
match &mut layer.data {
|
||||
LayerDataType::Image(image) => {
|
||||
image.blob_url = Some(blob_url);
|
||||
image.dimensions = resolution.into();
|
||||
}
|
||||
LayerDataType::NodeGraphFrame(node_graph_frame) => {
|
||||
node_graph_frame.blob_url = Some(blob_url);
|
||||
node_graph_frame.dimensions = resolution.into();
|
||||
}
|
||||
_ => panic!("Incorrectly trying to set the image blob URL for a layer that is not an Image, NodeGraphFrame or Imaginate layer type"),
|
||||
}
|
||||
let layer = self.layer_mut(&layer_path).unwrap_or_else(|_| panic!("Blob URL for invalid layer with path '{:?}'", layer_path));
|
||||
|
||||
let LayerDataType::NodeGraphFrame(node_graph_frame) = &mut layer.data else {
|
||||
panic!("Incorrectly trying to set the image blob URL for a layer that is not a NodeGraphFrame layer type");
|
||||
};
|
||||
|
||||
node_graph_frame.blob_url = Some(blob_url);
|
||||
node_graph_frame.dimensions = resolution.into();
|
||||
|
||||
self.mark_as_dirty(&layer_path)?;
|
||||
Some([vec![DocumentChanged, LayerChanged { path: layer_path.clone() }], update_thumbnails_upstream(&layer_path)].concat())
|
||||
@@ -1099,7 +1080,7 @@ impl Document {
|
||||
// Delete the layer if there are no longer any manipulator groups
|
||||
if (shape.manipulator_groups().len() - 1) == 0 {
|
||||
// Delegate deletion to DeleteLayer to update Layer Tree in frontend
|
||||
match self.handle_operation(Operation::DeleteLayer { path: layer_path.clone() }, font_cache) {
|
||||
match self.handle_operation(Operation::DeleteLayer { path: layer_path.clone() }, render_data) {
|
||||
Ok(Some(delete_responses)) => {
|
||||
responses.extend(delete_responses);
|
||||
responses.push(DocumentResponse::DeletedSelectedManipulatorPoints);
|
||||
@@ -1169,8 +1150,8 @@ fn update_thumbnails_upstream(path: &[LayerId]) -> Vec<DocumentResponse> {
|
||||
responses
|
||||
}
|
||||
|
||||
pub fn pick_layer_safe_imaginate_resolution(layer: &Layer, font_cache: &FontCache) -> (u64, u64) {
|
||||
let layer_bounds = layer.bounding_transform(font_cache);
|
||||
pub fn pick_layer_safe_imaginate_resolution(layer: &Layer, render_data: &RenderData) -> (u64, u64) {
|
||||
let layer_bounds = layer.bounding_transform(render_data);
|
||||
let layer_bounds_size = (layer_bounds.transform_vector2((1., 0.).into()).length(), layer_bounds.transform_vector2((0., 1.).into()).length());
|
||||
|
||||
pick_safe_imaginate_resolution(layer_bounds_size)
|
||||
|
||||
@@ -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])])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -45,13 +45,6 @@ pub enum Operation {
|
||||
font_name: String,
|
||||
font_style: String,
|
||||
},
|
||||
AddImage {
|
||||
path: Vec<LayerId>,
|
||||
insert_index: isize,
|
||||
transform: [f64; 6],
|
||||
mime: String,
|
||||
image_data: Vec<u8>,
|
||||
},
|
||||
AddNodeGraphFrame {
|
||||
path: Vec<LayerId>,
|
||||
insert_index: isize,
|
||||
|
||||
Reference in New Issue
Block a user