mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Centralize attribute strings in consts and rename "editor:layer" to "editor:layer_path" (#4076)
* Rename "editor:layer" to "editor:layer_path" and centralize it in a const * Centralize "editor:merged_layers" in a const * Centralize all other attributes in consts * Rename consts with ATTR_ prefix * Format
This commit is contained in:
@@ -33,6 +33,7 @@ pub use num_traits;
|
||||
use std::any::TypeId;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
pub use table::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_END, ATTR_NAME, ATTR_START, ATTR_TRANSFORM, ATTR_TYPE};
|
||||
#[cfg(feature = "wasm")]
|
||||
pub use tsify;
|
||||
pub use types::Cow;
|
||||
|
||||
@@ -5,6 +5,45 @@ use dyn_any::{StaticType, StaticTypeSized};
|
||||
use glam::DAffine2;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// =====================================================================
|
||||
// Standard attribute keys used across the data flow
|
||||
// =====================================================================
|
||||
|
||||
/// Attribute key for a row's `DAffine2` transformation, applied when rendering and when accumulating
|
||||
/// transforms through nested compositions.
|
||||
pub const ATTR_TRANSFORM: &str = "transform";
|
||||
|
||||
/// Attribute key for a row's `AlphaBlending` (blend mode + opacity + fill + clip), composed
|
||||
/// multiplicatively through nested compositions.
|
||||
pub const ATTR_ALPHA_BLENDING: &str = "alpha_blending";
|
||||
|
||||
/// Attribute key under which each row of an editor-aware layer stores a `Table<NodeId>` describing the
|
||||
/// path (from the root document network) to the layer node that owns the row. Editor tools use this to
|
||||
/// route clicks/selection back to the originating layer at any nesting depth.
|
||||
pub const ATTR_EDITOR_LAYER_PATH: &str = "editor:layer_path";
|
||||
|
||||
/// Attribute key under which a row stores a `Table<Graphic>` snapshot of the upstream content that fed
|
||||
/// into a destructive merge (Boolean Operation, Flatten Path, Morph, Rasterize, etc.). The renderer
|
||||
/// recurses into this snapshot during metadata collection so the editor can still surface click targets
|
||||
/// for the original child layers after their content has been collapsed into a single output.
|
||||
pub const ATTR_EDITOR_MERGED_LAYERS: &str = "editor:merged_layers";
|
||||
|
||||
/// Attribute key for the byte offset where a regex match begins in the input string, set by the
|
||||
/// `regex_find_all` and `regex_capture` text nodes.
|
||||
pub const ATTR_START: &str = "start";
|
||||
|
||||
/// Attribute key for the byte offset where a regex match ends in the input string, set by the
|
||||
/// `regex_find_all` and `regex_capture` text nodes.
|
||||
pub const ATTR_END: &str = "end";
|
||||
|
||||
/// Attribute key for a regex named-capture-group's name (empty for unnamed groups), set by the
|
||||
/// `regex_capture` text node.
|
||||
pub const ATTR_NAME: &str = "name";
|
||||
|
||||
/// Attribute key for a JSON value's type (`"string"`, `"number"`, `"object"`, etc.), set by the
|
||||
/// `json_query_all` text node alongside each extracted value.
|
||||
pub const ATTR_TYPE: &str = "type";
|
||||
|
||||
// =====================
|
||||
// TRAIT: AttributeValue
|
||||
// =====================
|
||||
@@ -747,7 +786,7 @@ impl<T: BoundingBox> BoundingBox for Table<T> {
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
||||
let mut combined_bounds = None;
|
||||
|
||||
for (element, row_transform) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<DAffine2>("transform")) {
|
||||
for (element, row_transform) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM)) {
|
||||
match element.bounding_box(transform * row_transform, include_stroke) {
|
||||
RenderBoundingBox::None => continue,
|
||||
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
||||
@@ -793,10 +832,10 @@ impl<T: graphene_hash::CacheHash> graphene_hash::CacheHash for Table<T> {
|
||||
for element in self.iter_element_values() {
|
||||
element.cache_hash(state);
|
||||
}
|
||||
for transform in self.iter_attribute_values_or_default::<DAffine2>("transform") {
|
||||
for transform in self.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
graphene_hash::CacheHash::cache_hash(&transform, state);
|
||||
}
|
||||
for alpha_blending in self.iter_attribute_values_or_default::<crate::AlphaBlending>("alpha_blending") {
|
||||
for alpha_blending in self.iter_attribute_values_or_default::<crate::AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||
alpha_blending.cache_hash(state);
|
||||
}
|
||||
}
|
||||
@@ -811,14 +850,14 @@ impl<T: PartialEq> PartialEq for Table<T> {
|
||||
impl<T> ApplyTransform for Table<T> {
|
||||
/// Right-multiplies the modification into each row's transform attribute.
|
||||
fn apply_transform(&mut self, modification: &DAffine2) {
|
||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform *= *modification;
|
||||
}
|
||||
}
|
||||
|
||||
/// Left-multiplies the modification into each row's transform attribute.
|
||||
fn left_apply_transform(&mut self, modification: &DAffine2) {
|
||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform = *modification * *transform;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::graphic::Graphic;
|
||||
use core_types::Color;
|
||||
use core_types::blending::AlphaBlending;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::math::quad::Quad;
|
||||
@@ -7,6 +6,7 @@ use core_types::render_complexity::RenderComplexity;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use core_types::transform::Transform;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_TRANSFORM, Color};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use graphene_hash::CacheHash;
|
||||
@@ -52,7 +52,7 @@ impl BoundingBox for Artboard {
|
||||
|
||||
let mut combined_bounds = None;
|
||||
|
||||
for (element, row_transform) in self.content.iter_element_values().zip(self.content.iter_attribute_values_or_default::<DAffine2>("transform")) {
|
||||
for (element, row_transform) in self.content.iter_element_values().zip(self.content.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM)) {
|
||||
match element.bounding_box(transform * row_transform, include_stroke) {
|
||||
RenderBoundingBox::None => continue,
|
||||
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
||||
@@ -113,7 +113,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
||||
alpha_blending: Vec<AlphaBlending>,
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match ArtboardFormat::deserialize(deserializer)? {
|
||||
ArtboardFormat::ArtboardGroup(artboard_group) => artboard_group.artboards.into_iter().map(|(artboard, _)| TableRow::new_from_element(artboard)).collect(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use core_types::Color;
|
||||
use core_types::blending::AlphaBlending;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::graphene_hash::CacheHash;
|
||||
@@ -6,6 +5,7 @@ use core_types::ops::TableConvert;
|
||||
use core_types::render_complexity::RenderComplexity;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Color};
|
||||
use dyn_any::DynAny;
|
||||
use glam::DAffine2;
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
@@ -141,19 +141,19 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||
|
||||
fn flatten_recursive<T>(output: &mut Table<T>, current_graphic_table: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) {
|
||||
for current_graphic_row in current_graphic_table.into_iter() {
|
||||
let layer_path: Table<NodeId> = current_graphic_row.attribute_cloned_or_default("editor:layer");
|
||||
let current_transform: DAffine2 = current_graphic_row.attribute_cloned_or_default("transform");
|
||||
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default("alpha_blending");
|
||||
let layer_path: Table<NodeId> = current_graphic_row.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
|
||||
let current_transform: DAffine2 = current_graphic_row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
|
||||
|
||||
match current_graphic_row.into_element() {
|
||||
// Recurse into nested `Table<Graphic>` items, composing the parent's transform onto each child
|
||||
Graphic::Graphic(mut sub_table) => {
|
||||
for index in 0..sub_table.len() {
|
||||
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default("transform", index);
|
||||
let child_alpha_blending: AlphaBlending = sub_table.attribute_cloned_or_default("alpha_blending", index);
|
||||
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let child_alpha_blending: AlphaBlending = sub_table.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
|
||||
sub_table.set_attribute("transform", index, current_transform * child_transform);
|
||||
sub_table.set_attribute("alpha_blending", index, compose_alpha_blending(current_alpha_blending, child_alpha_blending));
|
||||
sub_table.set_attribute(ATTR_TRANSFORM, index, current_transform * child_transform);
|
||||
sub_table.set_attribute(ATTR_ALPHA_BLENDING, index, compose_alpha_blending(current_alpha_blending, child_alpha_blending));
|
||||
}
|
||||
|
||||
flatten_recursive(output, sub_table, extract_variant);
|
||||
@@ -162,13 +162,13 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||
other => {
|
||||
if let Some(typed_table) = extract_variant(other) {
|
||||
for row in typed_table.into_iter() {
|
||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
||||
let row_alpha_blending: AlphaBlending = row.attribute_cloned_or_default("alpha_blending");
|
||||
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||
let row_alpha_blending: AlphaBlending = row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
|
||||
let (element, mut attributes) = row.into_parts();
|
||||
|
||||
attributes.insert("transform", current_transform * row_transform);
|
||||
attributes.insert("alpha_blending", compose_alpha_blending(current_alpha_blending, row_alpha_blending));
|
||||
attributes.insert("editor:layer", layer_path.clone());
|
||||
attributes.insert(ATTR_TRANSFORM, current_transform * row_transform);
|
||||
attributes.insert(ATTR_ALPHA_BLENDING, compose_alpha_blending(current_alpha_blending, row_alpha_blending));
|
||||
attributes.insert(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
|
||||
|
||||
output.push(TableRow::from_parts(element, attributes));
|
||||
}
|
||||
@@ -321,7 +321,7 @@ impl Graphic {
|
||||
|
||||
pub fn had_clip_enabled(&self) -> bool {
|
||||
fn all_clipped<T>(table: &Table<T>) -> bool {
|
||||
table.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending").all(|a| a.clip)
|
||||
table.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING).all(|a| a.clip)
|
||||
}
|
||||
match self {
|
||||
Graphic::Vector(table) => all_clipped(table),
|
||||
@@ -337,7 +337,7 @@ impl Graphic {
|
||||
match self {
|
||||
Graphic::Vector(vector) => vector
|
||||
.iter_element_values()
|
||||
.zip(vector.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending"))
|
||||
.zip(vector.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING))
|
||||
.all(|(element, alpha_blending)| {
|
||||
(alpha_blending.opacity > 1. - f32::EPSILON) && element.style.fill().is_opaque() && element.style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||
}),
|
||||
@@ -504,7 +504,7 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
||||
Table(serde_json::Value),
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match GraphicFormat::deserialize(deserializer)? {
|
||||
GraphicFormat::OldGraphicGroup(old) => old.elements.into_iter().map(|(graphic, _)| TableRow::new_from_element(graphic)).collect(),
|
||||
|
||||
@@ -72,7 +72,7 @@ pub mod migrations {
|
||||
|
||||
Ok(match VectorFormat::deserialize(deserializer)? {
|
||||
VectorFormat::Vector(vector) => Table::new_from_element(vector),
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
VectorFormat::OldVectorData(old) => Table::new_from_element(Vector {
|
||||
style: old.style,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::raster_types::{CPU, Raster};
|
||||
use crate::{Bitmap, BitmapMut};
|
||||
use core_types::AlphaBlending;
|
||||
use core_types::Color;
|
||||
use core_types::color::float_to_srgb_u8;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use core_types::{ATTR_ALPHA_BLENDING, ATTR_TRANSFORM, AlphaBlending, Color};
|
||||
// use crate::vector::Vector; // TODO: Check if Vector is actually used, if so handle differently
|
||||
use core_types::color::*;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
@@ -319,7 +318,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
Table::new_from_element(Raster::new_cpu(table.element(0).unwrap().clone()))
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
fn old_table_to_new_table<T>(old_table: OldTable<T>) -> Table<T> {
|
||||
old_table.element.into_iter().map(TableRow::new_from_element).collect()
|
||||
@@ -339,8 +338,8 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
FormatVersions::Image(image) => Table::new_from_element(Raster::new_cpu(image)),
|
||||
FormatVersions::OldImageFrame(OldImageFrame { image, transform, alpha_blending }) => {
|
||||
let mut image_frame_table = Table::new_from_element(Raster::new_cpu(image));
|
||||
image_frame_table.set_attribute("transform", 0, transform);
|
||||
image_frame_table.set_attribute("alpha_blending", 0, alpha_blending);
|
||||
image_frame_table.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||
image_frame_table.set_attribute(ATTR_ALPHA_BLENDING, 0, alpha_blending);
|
||||
image_frame_table
|
||||
}
|
||||
FormatVersions::OlderImageFrameTable(old_table) => from_image_frame_table(older_table_to_new_table(old_table)),
|
||||
@@ -430,7 +429,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
||||
RasterTableRow(TableRow<Raster<CPU>>),
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||
// to recover the element. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => TableRow::new_from_element(Raster::new_cpu(image)),
|
||||
|
||||
@@ -10,6 +10,7 @@ use core_types::render_complexity::RenderComplexity;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use core_types::transform::{Footprint, Transform};
|
||||
use core_types::uuid::{NodeId, generate_uuid};
|
||||
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_hash::CacheHashWrapper;
|
||||
@@ -412,9 +413,9 @@ impl Render for Graphic {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
let layer_path: Table<NodeId> = table.attribute_cloned_or_default("editor:layer", 0);
|
||||
let layer_path: Table<NodeId> = table.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0);
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
let transform: DAffine2 = table.attribute_cloned_or_default("transform", 0);
|
||||
let transform: DAffine2 = table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
|
||||
metadata.first_element_source_id.insert(element_id, layer);
|
||||
metadata.local_transforms.insert(element_id, transform);
|
||||
@@ -425,7 +426,7 @@ impl Render for Graphic {
|
||||
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||
}
|
||||
}
|
||||
Graphic::RasterGPU(table) => {
|
||||
@@ -433,7 +434,7 @@ impl Render for Graphic {
|
||||
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||
}
|
||||
}
|
||||
Graphic::Color(table) => {
|
||||
@@ -441,7 +442,7 @@ impl Render for Graphic {
|
||||
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||
}
|
||||
}
|
||||
Graphic::Gradient(table) => {
|
||||
@@ -449,7 +450,7 @@ impl Render for Graphic {
|
||||
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,7 +552,7 @@ impl Render for Artboard {
|
||||
|attributes| {
|
||||
let matrix = format_transform_matrix(self.transform());
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
|
||||
if self.clip {
|
||||
@@ -656,7 +657,7 @@ impl Render for Table<Artboard> {
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
||||
for index in 0..self.len() {
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
self.element(index).unwrap().collect_metadata(metadata, footprint, layer);
|
||||
}
|
||||
@@ -678,8 +679,8 @@ impl Render for Table<Graphic> {
|
||||
let mut mask_state = None;
|
||||
|
||||
for index in 0..self.len() {
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
let element = self.element(index).unwrap();
|
||||
|
||||
render.parent_tag(
|
||||
@@ -687,7 +688,7 @@ impl Render for Table<Graphic> {
|
||||
|attributes| {
|
||||
let matrix = format_transform_matrix(transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
|
||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||
@@ -732,9 +733,9 @@ impl Render for Table<Graphic> {
|
||||
let mut mask_element_and_transform = None;
|
||||
|
||||
for index in 0..self.len() {
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let transform = transform * row_transform;
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
let element = self.element(index).unwrap();
|
||||
|
||||
let mut layer = false;
|
||||
@@ -806,8 +807,8 @@ impl Render for Table<Graphic> {
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
for index in 0..self.len() {
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
let element = self.element(index).unwrap();
|
||||
|
||||
@@ -817,7 +818,7 @@ impl Render for Table<Graphic> {
|
||||
if let Some(element_id) = layer {
|
||||
element.collect_metadata(metadata, footprint, Some(element_id));
|
||||
} else {
|
||||
// Recurse through anonymous wrapper items to reach nested content with editor:layer tags
|
||||
// Recurse through anonymous wrapper items to reach nested content with editor:layer_path tags
|
||||
element.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
}
|
||||
@@ -826,7 +827,7 @@ impl Render for Table<Graphic> {
|
||||
let mut all_upstream_click_targets = Vec::new();
|
||||
|
||||
for index in 0..self.len() {
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let element = self.element(index).unwrap();
|
||||
let mut new_click_targets = Vec::new();
|
||||
element.add_upstream_click_targets(&mut new_click_targets);
|
||||
@@ -844,7 +845,7 @@ impl Render for Table<Graphic> {
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
for index in 0..self.len() {
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let element = self.element(index).unwrap();
|
||||
let mut new_click_targets = Vec::new();
|
||||
|
||||
@@ -863,7 +864,7 @@ impl Render for Table<Graphic> {
|
||||
}
|
||||
|
||||
fn new_ids_from_hash(&mut self, _reference: Option<NodeId>) {
|
||||
let (elements, layers) = self.element_and_attribute_slices_mut::<Table<NodeId>>("editor:layer");
|
||||
let (elements, layers) = self.element_and_attribute_slices_mut::<Table<NodeId>>(ATTR_EDITOR_LAYER_PATH);
|
||||
for (element, layer) in elements.iter_mut().zip(layers.iter()) {
|
||||
element.new_ids_from_hash(layer.iter_element_values().next_back().copied());
|
||||
}
|
||||
@@ -874,8 +875,8 @@ impl Render for Table<Vector> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for index in 0..self.len() {
|
||||
let Some(vector) = self.element(index) else { continue };
|
||||
let multiplied_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let multiplied_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
|
||||
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
||||
let has_real_stroke = vector.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
@@ -915,7 +916,7 @@ impl Render for Table<Vector> {
|
||||
attributes.push("d", path.clone());
|
||||
let matrix = format_transform_matrix(element_transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
let mut style = vector.style.clone();
|
||||
style.clear_stroke();
|
||||
@@ -940,8 +941,8 @@ impl Render for Table<Vector> {
|
||||
|
||||
let vector_row = Table::new_from_row(
|
||||
TableRow::new_from_element(cloned_vector)
|
||||
.with_attribute("transform", multiplied_transform)
|
||||
.with_attribute("alpha_blending", alpha_blending),
|
||||
.with_attribute(ATTR_TRANSFORM, multiplied_transform)
|
||||
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending),
|
||||
);
|
||||
|
||||
(id, mask_type, vector_row)
|
||||
@@ -957,7 +958,7 @@ impl Render for Table<Vector> {
|
||||
attributes.push("d", face_d.clone());
|
||||
let matrix = format_transform_matrix(element_transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
let mut style = vector.style.clone();
|
||||
style.clear_stroke();
|
||||
@@ -978,7 +979,7 @@ impl Render for Table<Vector> {
|
||||
attributes.push("d", path.clone());
|
||||
let matrix = format_transform_matrix(element_transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
|
||||
let defs = &mut attributes.0.svg_defs;
|
||||
@@ -1043,7 +1044,7 @@ impl Render for Table<Vector> {
|
||||
attributes.push("d", path);
|
||||
let matrix = format_transform_matrix(element_transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
let mut style = vector.style.clone();
|
||||
style.clear_stroke();
|
||||
@@ -1068,8 +1069,8 @@ impl Render for Table<Vector> {
|
||||
use graphic_types::vector_types::vector;
|
||||
|
||||
let Some(element) = self.element(index) else { continue };
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
let multiplied_transform = parent_transform * row_transform;
|
||||
let has_real_stroke = element.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
|
||||
@@ -1257,8 +1258,8 @@ impl Render for Table<Vector> {
|
||||
|
||||
let vector_table = Table::new_from_row(
|
||||
TableRow::new_from_element(cloned_element)
|
||||
.with_attribute("transform", row_transform)
|
||||
.with_attribute("alpha_blending", alpha_blending),
|
||||
.with_attribute(ATTR_TRANSFORM, row_transform)
|
||||
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending),
|
||||
);
|
||||
|
||||
let bounds = element.bounding_box_with_transform(multiplied_transform).unwrap_or(layer_bounds);
|
||||
@@ -1329,12 +1330,12 @@ impl Render for Table<Vector> {
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||
for index in 0..self.len() {
|
||||
let Some(vector) = self.element(index) else { continue };
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
|
||||
if let Some(element_id) = caller_element_id.or(layer) {
|
||||
// When recovering element_id from the item's editor:layer tag (because the caller
|
||||
// When recovering element_id from the item's editor:layer_path tag (because the caller
|
||||
// passed None), also store the transform metadata that Graphic::collect_metadata
|
||||
// normally provides but skipped due to the None element_id.
|
||||
if caller_element_id.is_none() {
|
||||
@@ -1378,7 +1379,7 @@ impl Render for Table<Vector> {
|
||||
// If this item carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
||||
// Flatten Path, Morph, or any other destructive merge), recurse into that snapshot so the editor can
|
||||
// surface the original child layers' click targets.
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", index);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, index);
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
let mut upstream_footprint = footprint;
|
||||
upstream_footprint.transform *= transform;
|
||||
@@ -1390,7 +1391,7 @@ impl Render for Table<Vector> {
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
for index in 0..self.len() {
|
||||
let Some(vector) = self.element(index) else { continue };
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
|
||||
let stroke_width = vector.style.stroke().as_ref().map_or(0., Stroke::effective_width);
|
||||
let filled = vector.style.fill() != &Fill::None;
|
||||
@@ -1435,8 +1436,8 @@ impl Render for Table<Raster<CPU>> {
|
||||
for index in 0..self.len() {
|
||||
let Some(image) = self.element(index) else { continue };
|
||||
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
|
||||
if image.data.is_empty() {
|
||||
continue;
|
||||
@@ -1457,7 +1458,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
let matrix = DAffine2::from_scale_angle_translation(transform_values.0, transform_values.1, transform_values.2);
|
||||
let matrix = format_transform_matrix(matrix);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
|
||||
attributes.push("width", size.x.to_string());
|
||||
@@ -1500,7 +1501,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
attributes.push("href", base64_string);
|
||||
let matrix = format_transform_matrix(transform);
|
||||
if !matrix.is_empty() {
|
||||
attributes.push("transform", matrix);
|
||||
attributes.push(ATTR_TRANSFORM, matrix);
|
||||
}
|
||||
|
||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||
@@ -1522,7 +1523,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||
|
||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||
@@ -1537,7 +1538,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
layer = true;
|
||||
}
|
||||
|
||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
|
||||
if let RenderMode::Outline = render_params.render_mode {
|
||||
let outline_transform: DAffine2 = transform * transform_attribute;
|
||||
@@ -1577,7 +1578,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||
if !self.is_empty() {
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
metadata.local_transforms.insert(element_id, transform);
|
||||
|
||||
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
||||
@@ -1586,7 +1587,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", 0);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
@@ -1609,7 +1610,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
for index in 0..self.len() {
|
||||
let Some(raster) = self.element(index) else { continue };
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
let blend_mode = match render_params.render_mode {
|
||||
RenderMode::Outline => peniko::Mix::Normal,
|
||||
_ => alpha_blending.blend_mode.to_peniko(),
|
||||
@@ -1626,7 +1627,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
layer = true;
|
||||
}
|
||||
|
||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
|
||||
if let RenderMode::Outline = render_params.render_mode {
|
||||
let outline_transform = transform * transform_attribute;
|
||||
@@ -1667,7 +1668,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||
if !self.is_empty() {
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
metadata.local_transforms.insert(element_id, transform);
|
||||
|
||||
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
||||
@@ -1676,7 +1677,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", 0);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
@@ -1697,7 +1698,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
// later replace with the current viewport transform before each render.
|
||||
impl Render for Table<Color> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||
render.leaf_tag("polyline", |attributes| {
|
||||
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
||||
let max = u64::MAX;
|
||||
@@ -1723,7 +1724,7 @@ impl Render for Table<Color> {
|
||||
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||
use vello::peniko;
|
||||
|
||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||
|
||||
@@ -1752,8 +1753,8 @@ impl Render for Table<GradientStops> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for index in 0..self.len() {
|
||||
let Some(gradient) = self.element(index) else { continue };
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||
render.leaf_tag("rect", |attributes| {
|
||||
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
||||
let max = u64::MAX;
|
||||
@@ -1820,7 +1821,7 @@ impl Render for Table<GradientStops> {
|
||||
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||
use vello::peniko;
|
||||
|
||||
for (gradient, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
||||
for (gradient, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Contains stylistic options for SVG elements.
|
||||
|
||||
pub use crate::gradient::*;
|
||||
use core_types::ATTR_ALPHA_BLENDING;
|
||||
use core_types::AlphaBlending;
|
||||
use core_types::Color;
|
||||
use core_types::color::Alpha;
|
||||
@@ -135,7 +136,7 @@ impl From<Option<Color>> for Fill {
|
||||
|
||||
impl From<Table<Color>> for Fill {
|
||||
fn from(color: Table<Color>) -> Fill {
|
||||
let alpha = color.attribute_cloned_or_default::<AlphaBlending>("alpha_blending", 0).opacity;
|
||||
let alpha = color.attribute_cloned_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING, 0).opacity;
|
||||
let color = color.element(0).copied();
|
||||
Fill::solid_or_none(color.map(|c| c.with_alpha(c.alpha() * alpha)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user