Image and text bug fixes (#685)

* Image and text bugfixes

* Mark only the required layer types as dirty

* Fix doctest

* Disable selection if empty

* Cleanup naming

* Simplify cache deleting on export

* Minor css style change

* Nit

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2022-06-30 02:18:01 +01:00
committed by GitHub
parent ee98908d88
commit 2191b09f9f
22 changed files with 243 additions and 128 deletions

View File

@@ -2,7 +2,7 @@ use crate::message_prelude::*;
use graphene::color::Color;
use graphene::document::Document as GrapheneDocument;
use graphene::layers::style::{self, Fill, ViewMode};
use graphene::layers::style::{self, Fill, RenderData, ViewMode};
use graphene::layers::text_layer::FontCache;
use graphene::DocumentResponse;
use graphene::Operation as DocumentOperation;
@@ -85,9 +85,10 @@ impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
.into(),
)
} else {
let render_data = RenderData::new(ViewMode::Normal, font_cache, None, false);
responses.push_back(
FrontendMessage::UpdateDocumentArtboards {
svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache, None),
svg: self.artboards_graphene_document.render_root(render_data),
}
.into(),
);

View File

@@ -1,5 +1,5 @@
use super::clipboards::Clipboard;
use super::layer_panel::{layer_panel_entry, LayerDataTypeDiscriminant, LayerMetadata, LayerPanelEntry, RawBuffer};
use super::layer_panel::{layer_panel_entry, LayerMetadata, LayerPanelEntry, RawBuffer};
use super::properties_panel_message_handler::PropertiesPanelMessageHandlerData;
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
use super::utility_types::{DocumentMode, TargetDocument};
@@ -21,16 +21,15 @@ use graphene::color::Color;
use graphene::document::Document as GrapheneDocument;
use graphene::layers::blend_mode::BlendMode;
use graphene::layers::folder_layer::FolderLayer;
use graphene::layers::layer_info::LayerDataType;
use graphene::layers::style::{Fill, ViewMode};
use graphene::layers::text_layer::FontCache;
use graphene::layers::layer_info::{LayerDataType, LayerDataTypeDiscriminant};
use graphene::layers::style::{Fill, RenderData, ViewMode};
use graphene::layers::text_layer::{Font, FontCache};
use graphene::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use glam::{DAffine2, DVec2};
use log::warn;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::VecDeque;
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DocumentMessageHandler {
@@ -491,18 +490,20 @@ impl DocumentMessageHandler {
path
}
/// Creates the blob URLs for the image data in the document
pub fn load_image_data(&self, responses: &mut VecDeque<Message>, root: &LayerDataType, mut path: Vec<LayerId>) {
let mut image_data = Vec::new();
fn walk_layers(data: &LayerDataType, path: &mut Vec<LayerId>, image_data: &mut Vec<FrontendImageData>) {
/// Loads layer resources such as creating the blob URLs for the images and loading all of the fonts in the document
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>, root: &LayerDataType, mut path: Vec<LayerId>) {
fn walk_layers(data: &LayerDataType, path: &mut Vec<LayerId>, image_data: &mut Vec<FrontendImageData>, fonts: &mut HashSet<Font>) {
match data {
LayerDataType::Folder(f) => {
for (id, layer) in f.layer_ids.iter().zip(f.layers().iter()) {
path.push(*id);
walk_layers(&layer.data, path, image_data);
walk_layers(&layer.data, path, image_data, fonts);
path.pop();
}
}
LayerDataType::Text(txt) => {
fonts.insert(txt.font.clone());
}
LayerDataType::Image(img) => image_data.push(FrontendImageData {
path: path.clone(),
image_data: img.image_data.clone(),
@@ -512,10 +513,15 @@ impl DocumentMessageHandler {
}
}
walk_layers(root, &mut path, &mut image_data);
let mut image_data = Vec::new();
let mut fonts = HashSet::new();
walk_layers(root, &mut path, &mut image_data, &mut fonts);
if !image_data.is_empty() {
responses.push_front(FrontendMessage::UpdateImageData { image_data }.into());
}
for font in fonts {
responses.push_front(FrontendMessage::TriggerFontLoad { font, is_default: false }.into());
}
}
pub fn update_document_widgets(&self, responses: &mut VecDeque<Message>) {
@@ -1040,12 +1046,14 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
let old_transform = self.graphene_document.root.transform;
// Reset the root's transform (required to avoid any rotation by the user)
self.graphene_document.root.transform = DAffine2::IDENTITY;
self.graphene_document.root.cache_dirty = true;
GrapheneDocument::mark_children_as_dirty(&mut self.graphene_document.root);
// Calculates the bounding box of the region to be exported
use crate::frontend::utility_types::ExportBounds;
let bbox = match bounds {
crate::frontend::utility_types::ExportBounds::AllArtwork => self.all_layer_bounds(font_cache),
crate::frontend::utility_types::ExportBounds::Artboard(id) => self
ExportBounds::AllArtwork => self.all_layer_bounds(font_cache),
ExportBounds::Selection => self.selected_visible_layers_bounding_box(font_cache),
ExportBounds::Artboard(id) => self
.artboard_message_handler
.artboards_graphene_document
.layer(&[id])
@@ -1061,14 +1069,15 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
false => file_name + file_suffix,
};
let rendered = self.graphene_document.render_root(self.view_mode, font_cache, None);
let render_data = RenderData::new(self.view_mode, font_cache, None, true);
let rendered = self.graphene_document.render_root(render_data);
let document = format!(
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="{} {} {} {}" width="{}px" height="{}">{}{}</svg>"#,
bbox[0].x, bbox[0].y, size.x, size.y, size.x, size.y, "\n", rendered
);
self.graphene_document.root.transform = old_transform;
self.graphene_document.root.cache_dirty = true;
GrapheneDocument::mark_children_as_dirty(&mut self.graphene_document.root);
if file_type == FileType::Svg {
responses.push_back(FrontendMessage::TriggerFileDownload { document, name }.into());
@@ -1218,9 +1227,10 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
}
RenameLayer { layer_path, new_name } => responses.push_back(DocumentOperation::RenameLayer { layer_path, new_name }.into()),
RenderDocument => {
let render_data = RenderData::new(self.view_mode, font_cache, Some(ipp.document_bounds()), false);
responses.push_back(
FrontendMessage::UpdateDocumentArtwork {
svg: self.graphene_document.render_root(self.view_mode, font_cache, Some(ipp.document_bounds())),
svg: self.graphene_document.render_root(render_data),
}
.into(),
);

View File

@@ -1,12 +1,11 @@
use graphene::layers::layer_info::{Layer, LayerData, LayerDataType};
use graphene::layers::style::ViewMode;
use graphene::layers::layer_info::{Layer, LayerData, LayerDataTypeDiscriminant};
use graphene::layers::style::{RenderData, ViewMode};
use graphene::layers::text_layer::FontCache;
use graphene::LayerId;
use glam::{DAffine2, DVec2};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy)]
pub struct LayerMetadata {
@@ -27,7 +26,8 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la
let mut thumbnail = String::new();
let mut svg_defs = String::new();
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], ViewMode::Normal, font_cache, None);
let render_data = RenderData::new(ViewMode::Normal, font_cache, None, false);
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], render_data);
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {
format!(
@@ -90,37 +90,3 @@ pub struct LayerPanelEntry {
pub path: Vec<LayerId>,
pub thumbnail: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum LayerDataTypeDiscriminant {
Folder,
Shape,
Text,
Image,
}
impl fmt::Display for LayerDataTypeDiscriminant {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
LayerDataTypeDiscriminant::Folder => "Folder",
LayerDataTypeDiscriminant::Shape => "Shape",
LayerDataTypeDiscriminant::Text => "Text",
LayerDataTypeDiscriminant::Image => "Image",
};
formatter.write_str(name)
}
}
impl From<&LayerDataType> for LayerDataTypeDiscriminant {
fn from(data: &LayerDataType) -> Self {
use LayerDataType::*;
match data {
Folder(_) => LayerDataTypeDiscriminant::Folder,
Shape(_) => LayerDataTypeDiscriminant::Shape,
Text(_) => LayerDataTypeDiscriminant::Text,
Image(_) => LayerDataTypeDiscriminant::Image,
}
}
}

View File

@@ -2,7 +2,7 @@ use crate::input::InputPreprocessorMessageHandler;
use crate::message_prelude::*;
use graphene::document::Document as GrapheneDocument;
use graphene::layers::style::ViewMode;
use graphene::layers::style::{RenderData, ViewMode};
use graphene::layers::text_layer::FontCache;
#[derive(Debug, Clone, Default)]
@@ -32,7 +32,8 @@ impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessag
responses.push_back(
FrontendMessage::UpdateDocumentOverlays {
svg: if overlays_visible {
self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache, Some(ipp.document_bounds()))
let render_data = RenderData::new(ViewMode::Normal, font_cache, Some(ipp.document_bounds()), false);
self.overlays_graphene_document.render_root(render_data)
} else {
String::from("")
},

View File

@@ -7,6 +7,7 @@ use crate::layout::layout_message::LayoutTarget;
use crate::layout::widgets::PropertyHolder;
use crate::{dialog, message_prelude::*};
use graphene::layers::layer_info::LayerDataTypeDiscriminant;
use graphene::layers::text_layer::{Font, FontCache};
use graphene::Operation as DocumentOperation;
@@ -47,11 +48,10 @@ impl PortfolioMessageHandler {
// Uses binary search to find the index of the element where number is bigger than i
let new_doc_title_num = doc_title_numbers.binary_search(&0).map_or_else(|e| e, |v| v) + 1;
let name = match new_doc_title_num {
match new_doc_title_num {
1 => DEFAULT_DOCUMENT_NAME.to_string(),
_ => format!("{} {}", DEFAULT_DOCUMENT_NAME, new_doc_title_num),
};
name
}
}
// TODO Fix how this doesn't preserve tab order upon loading new document from *File > Load*
@@ -81,7 +81,7 @@ impl PortfolioMessageHandler {
);
new_document.update_layer_tree_options_bar_widgets(responses, &self.font_cache);
new_document.load_image_data(responses, &new_document.graphene_document.root.data, Vec::new());
new_document.load_layer_resources(responses, &new_document.graphene_document.root.data, Vec::new());
self.documents.insert(document_id, new_document);
@@ -287,7 +287,8 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
is_default,
} => {
self.font_cache.insert(Font::new(font_family, font_style), preview_url, data, is_default);
responses.push_back(DocumentMessage::DirtyRenderDocument.into());
self.active_document_mut().graphene_document.mark_all_layers_of_type_as_dirty(LayerDataTypeDiscriminant::Text);
responses.push_back(DocumentMessage::RenderDocument.into());
}
LoadFont { font, is_default } => {
if !self.font_cache.loaded_font(&font) {
@@ -387,7 +388,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
}
.into(),
);
self.active_document().load_image_data(responses, &entry.layer.data, destination_path.clone());
self.active_document().load_layer_resources(responses, &entry.layer.data, destination_path.clone());
responses.push_front(
DocumentOperation::InsertLayer {
layer: entry.layer.clone(),
@@ -428,7 +429,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
}
.into(),
);
self.active_document().load_image_data(responses, &entry.layer.data, destination_path.clone());
self.active_document().load_layer_resources(responses, &entry.layer.data, destination_path.clone());
responses.push_front(
DocumentOperation::InsertLayer {
layer: entry.layer.clone(),

View File

@@ -1,4 +1,3 @@
use super::layer_panel::LayerDataTypeDiscriminant;
use super::utility_types::TargetDocument;
use crate::document::properties_panel_message::TransformOp;
use crate::layout::layout_message::LayoutTarget;
@@ -10,7 +9,7 @@ use crate::message_prelude::*;
use graphene::color::Color;
use graphene::document::Document as GrapheneDocument;
use graphene::layers::layer_info::{Layer, LayerDataType};
use graphene::layers::layer_info::{Layer, LayerDataType, LayerDataTypeDiscriminant};
use graphene::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke};
use graphene::layers::text_layer::{FontCache, TextLayer};
use graphene::{LayerId, Operation};