Skip to content

Commit 4474de4

Browse files
authored
Replace the AlphaBlending struct with separate attributes (#4086)
* Replace the AlphaBlending struct with separate attributes * Fix bug * Fix bug
1 parent 86134c2 commit 4474de4

18 files changed

Lines changed: 374 additions & 295 deletions

File tree

demo-artwork/isometric-fountain.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-artwork/valley-of-spires.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -904,9 +904,7 @@ macro_rules! known_table_row_types {
904904
};
905905
}
906906

907-
/// Override hook for [`Table::attribute_display_value`] that prefers `Display` over `Debug` for select
908-
/// attribute types. The underlying storage is generic and can only see a `Debug` bound, so types whose
909-
/// nicer `Display` rendering matters in the data panel are listed here explicitly.
907+
/// Uses `Display` instead of `Debug` for attribute types that have a nicer human-readable format.
910908
fn display_value_override(any: &dyn Any) -> Option<String> {
911909
if let Some(value) = any.downcast_ref::<BlendMode>() {
912910
return Some(value.to_string());

editor/src/messages/portfolio/document/graph_operation/utility_types.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ impl<'a> ModifyInputsContext<'a> {
440440
let Some(blend_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blend_mode::IDENTIFIER, true) else {
441441
return;
442442
};
443-
let input_connector = InputConnector::node(blend_node_id, 1);
443+
let input_connector = InputConnector::node(blend_node_id, graphene_std::blending_nodes::blend_mode::BlendModeInput::INDEX);
444444
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false);
445445
}
446446

@@ -449,26 +449,45 @@ impl<'a> ModifyInputsContext<'a> {
449449
return;
450450
};
451451
// Enable the `has_opacity` checkbox so the value is applied
452-
self.set_input_with_refresh(InputConnector::node(opacity_node_id, 1), NodeInput::value(TaggedValue::Bool(true), false), false);
453-
self.set_input_with_refresh(InputConnector::node(opacity_node_id, 2), NodeInput::value(TaggedValue::F64(opacity * 100.), false), false);
452+
self.set_input_with_refresh(
453+
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput::INDEX),
454+
NodeInput::value(TaggedValue::Bool(true), false),
455+
false,
456+
);
457+
self.set_input_with_refresh(
458+
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::OpacityInput::INDEX),
459+
NodeInput::value(TaggedValue::F64(opacity * 100.), false),
460+
false,
461+
);
454462
}
455463

456464
pub fn opacity_fill_set(&mut self, fill: f64) {
457-
// Reuse the Opacity node if already present (saving a chain walk on slider drags), otherwise let the next call create it
465+
// Reuse an existing Opacity node to avoid a redundant chain walk on slider drags
458466
let identifier = graphene_std::blending_nodes::opacity::IDENTIFIER;
459467
let existing = self.existing_proto_node_id(identifier.clone(), false);
460468
let existed = existing.is_some();
461469
let Some(opacity_node_id) = existing.or_else(|| self.existing_proto_node_id(identifier, true)) else {
462470
return;
463471
};
464-
// Disable the opacity component on a freshly-created node so the slider only affects fill, mirroring the opacity-slider case
465-
// (where the node's default `has_fill = false` already keeps fill out of the picture)
472+
// Freshly-created node defaults to opacity enabled; disable it so the fill slider works independently
466473
if !existed {
467-
self.set_input_with_refresh(InputConnector::node(opacity_node_id, 1), NodeInput::value(TaggedValue::Bool(false), false), false);
474+
self.set_input_with_refresh(
475+
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput::INDEX),
476+
NodeInput::value(TaggedValue::Bool(false), false),
477+
false,
478+
);
468479
}
469480
// Enable the `has_fill` checkbox so the value is applied
470-
self.set_input_with_refresh(InputConnector::node(opacity_node_id, 3), NodeInput::value(TaggedValue::Bool(true), false), false);
471-
self.set_input_with_refresh(InputConnector::node(opacity_node_id, 4), NodeInput::value(TaggedValue::F64(fill * 100.), false), false);
481+
self.set_input_with_refresh(
482+
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasFillInput::INDEX),
483+
NodeInput::value(TaggedValue::Bool(true), false),
484+
false,
485+
);
486+
self.set_input_with_refresh(
487+
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::FillInput::INDEX),
488+
NodeInput::value(TaggedValue::F64(fill * 100.), false),
489+
false,
490+
);
472491
}
473492

474493
/// Set the stops table on the 'Gradient Value' node, creating it if necessary.
@@ -570,7 +589,7 @@ impl<'a> ModifyInputsContext<'a> {
570589
let Some(clip_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::clipping_mask::IDENTIFIER, true) else {
571590
return;
572591
};
573-
let input_connector = InputConnector::node(clip_node_id, 1);
592+
let input_connector = InputConnector::node(clip_node_id, graphene_std::blending_nodes::clipping_mask::ClipInput::INDEX);
574593
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Bool(clip), false), false);
575594
}
576595

node-graph/libraries/core-types/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use std::any::TypeId;
3434
use std::future::Future;
3535
use std::pin::Pin;
3636
pub use table::{
37-
ATTR_ALPHA_BLENDING, ATTR_BACKGROUND, ATTR_CLIP, ATTR_DIMENSIONS, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_END, ATTR_GRADIENT_TYPE, ATTR_LOCATION, ATTR_NAME, ATTR_SPREAD_METHOD,
38-
ATTR_START, ATTR_TRANSFORM, ATTR_TYPE,
37+
ATTR_BACKGROUND, ATTR_BLEND_MODE, ATTR_CLIP, ATTR_CLIPPING_MASK, ATTR_DIMENSIONS, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_END, ATTR_GRADIENT_TYPE, ATTR_LOCATION, ATTR_NAME,
38+
ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD, ATTR_START, ATTR_TRANSFORM, ATTR_TYPE,
3939
};
4040
#[cfg(feature = "wasm")]
4141
pub use tsify;

node-graph/libraries/core-types/src/table.rs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,59 +10,60 @@ use std::fmt::Debug;
1010
// Standard attribute keys used across the data flow
1111
// =====================================================================
1212

13-
/// Attribute key for a row's `DAffine2` transformation, applied when rendering and when accumulating
14-
/// transforms through nested compositions.
13+
/// Row's `DAffine2` transformation, composed multiplicatively through nested groups.
1514
pub const ATTR_TRANSFORM: &str = "transform";
1615

17-
/// Attribute key for a row's `AlphaBlending` (blend mode + opacity + fill + clip), composed
18-
/// multiplicatively through nested compositions.
19-
pub const ATTR_ALPHA_BLENDING: &str = "alpha_blending";
16+
/// Row's `BlendMode`, controlling how it composites with content beneath it.
17+
pub const ATTR_BLEND_MODE: &str = "blend_mode";
2018

21-
/// Attribute key under which each row of an editor-aware layer stores a `Table<NodeId>` describing the
22-
/// path (from the root document network) to the layer node that owns the row. Editor tools use this to
23-
/// route clicks/selection back to the originating layer at any nesting depth.
19+
/// Row's opacity multiplier (`f64`, implicit default `1.`).
20+
/// Composed multiplicatively through nested groups. Affects content clipped to the row.
21+
pub const ATTR_OPACITY: &str = "opacity";
22+
23+
/// Row's fill opacity multiplier (`f64`, implicit default `1.`).
24+
/// Like opacity but does not affect content clipped to the row.
25+
pub const ATTR_OPACITY_FILL: &str = "opacity_fill";
26+
27+
/// Whether a row inherits the alpha of the content beneath it (clipping mask).
28+
pub const ATTR_CLIPPING_MASK: &str = "clipping_mask";
29+
30+
/// `Table<NodeId>` path from the root network to the layer node owning this row.
31+
/// Used by editor tools to route clicks/selection back to the originating layer.
2432
pub const ATTR_EDITOR_LAYER_PATH: &str = "editor:layer_path";
2533

26-
/// Attribute key under which a row stores a `Table<Graphic>` snapshot of the upstream content that fed
27-
/// into a destructive merge (Boolean Operation, Flatten Path, Morph, Rasterize, etc.). The renderer
28-
/// recurses into this snapshot during metadata collection so the editor can still surface click targets
29-
/// for the original child layers after their content has been collapsed into a single output.
34+
/// `Table<Graphic>` snapshot of the upstream content that fed into a destructive merge
35+
/// (Boolean Operation, Rasterize, etc.), so the editor can still surface click targets for
36+
/// the original child layers after their content has been collapsed.
3037
pub const ATTR_EDITOR_MERGED_LAYERS: &str = "editor:merged_layers";
3138

32-
/// Attribute key for the byte offset where a regex match begins in the input string, set by the
33-
/// `regex_find_all` and `regex_capture` text nodes.
39+
/// Byte offset where a regex match begins ('Regex Find All', 'Regex Capture' text nodes).
3440
pub const ATTR_START: &str = "start";
3541

36-
/// Attribute key for the byte offset where a regex match ends in the input string, set by the
37-
/// `regex_find_all` and `regex_capture` text nodes.
42+
/// Byte offset where a regex match ends ('Regex Find All', 'Regex Capture' text nodes).
3843
pub const ATTR_END: &str = "end";
3944

40-
/// Attribute key for a regex named-capture-group's name (empty for unnamed groups), set by the
41-
/// `regex_capture` text node.
45+
/// Regex named-capture-group's name, or empty for unnamed groups ('Regex Capture' text node).
4246
pub const ATTR_NAME: &str = "name";
4347

44-
/// Attribute key for a JSON value's type (`"string"`, `"number"`, `"object"`, etc.), set by the
45-
/// `json_query_all` text node alongside each extracted value.
48+
/// JSON value's type string (`"string"`, `"number"`, `"object"`, etc.) from 'JSON Query All'.
4649
pub const ATTR_TYPE: &str = "type";
4750

48-
/// Attribute key for an artboard row's `DVec2` top-left corner location in document coordinates.
51+
/// Artboard's `DVec2` top-left corner in document coordinates.
4952
pub const ATTR_LOCATION: &str = "location";
5053

51-
/// Attribute key for an artboard row's `DVec2` width and height.
54+
/// Artboard's `DVec2` width and height.
5255
pub const ATTR_DIMENSIONS: &str = "dimensions";
5356

54-
/// Attribute key for an artboard row's `Color` background fill.
57+
/// Artboard's `Color` background fill.
5558
pub const ATTR_BACKGROUND: &str = "background";
5659

57-
/// Attribute key for an artboard row's `bool` flag indicating whether content is clipped to the artboard bounds.
60+
/// Whether an artboard clips content to its bounds.
5861
pub const ATTR_CLIP: &str = "clip";
5962

60-
/// Attribute key for a `Table<GradientStops>` row's `GradientSpreadMethod`, controlling the gradient's behavior
61-
/// outside the start/end stops (`Pad` clamps to the boundary colors, `Reflect` mirrors, `Repeat` tiles).
63+
/// Gradient's `GradientSpreadMethod` (`Pad`, `Reflect`, or `Repeat`).
6264
pub const ATTR_SPREAD_METHOD: &str = "spread_method";
6365

64-
/// Attribute key for a `Table<GradientStops>` row's `GradientType`, choosing between a linear gradient (color
65-
/// transitions along the gradient line) or a radial gradient (color transitions outward from the line's start).
66+
/// Gradient's `GradientType` (`Linear` or `Radial`).
6667
pub const ATTR_GRADIENT_TYPE: &str = "gradient_type";
6768

6869
// =====================

node-graph/libraries/graphic-types/src/artboard.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::graphic::Graphic;
2-
use core_types::blending::AlphaBlending;
2+
use core_types::blending::BlendMode;
33
use core_types::table::{Table, TableRow};
44
use core_types::uuid::NodeId;
55
use core_types::{ATTR_BACKGROUND, ATTR_CLIP, ATTR_DIMENSIONS, ATTR_LOCATION, Color};
@@ -22,6 +22,17 @@ use glam::{DAffine2, IVec2};
2222
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Table<Graphic>>, D::Error> {
2323
use serde::Deserialize;
2424

25+
/// Mirrors the removed `AlphaBlending` struct for legacy document deserialization.
26+
#[derive(Clone, Debug, Default)]
27+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28+
#[cfg_attr(feature = "serde", serde(default))]
29+
pub struct LegacyAlphaBlending {
30+
pub blend_mode: BlendMode,
31+
pub opacity: f32,
32+
pub fill: f32,
33+
pub clip: bool,
34+
}
35+
2536
/// Pre-migration shape of the artboard's stored data: the struct that used to live as the element
2637
/// of `Table<Artboard>`. Kept as a private type so we can deserialize legacy documents into the new
2738
/// `Table<Table<Graphic>>` (element = `content`, other fields → row attributes).
@@ -48,7 +59,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
4859
#[cfg_attr(feature = "serde", serde(alias = "instances", alias = "instance"))]
4960
element: Vec<T>,
5061
transform: Vec<DAffine2>,
51-
alpha_blending: Vec<AlphaBlending>,
62+
alpha_blending: Vec<LegacyAlphaBlending>,
5263
}
5364

5465
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

node-graph/libraries/graphic-types/src/graphic.rs

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use core_types::blending::AlphaBlending;
1+
use core_types::blending::BlendMode;
22
use core_types::bounds::{BoundingBox, RenderBoundingBox};
33
use core_types::graphene_hash::CacheHash;
44
use core_types::ops::TableConvert;
55
use core_types::render_complexity::RenderComplexity;
66
use core_types::table::{Table, TableRow};
77
use core_types::uuid::NodeId;
8-
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Color};
8+
use core_types::{ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Color};
99
use dyn_any::DynAny;
1010
use glam::DAffine2;
1111
use raster_types::{CPU, GPU, Raster};
@@ -130,47 +130,57 @@ impl From<Table<GradientStops>> for Graphic {
130130
/// Deeply flattens a `Table<Graphic>`, collecting only elements matching a specific variant (extracted by `extract_variant`)
131131
/// and discarding all other non-matching content. Recursion through `Graphic::Graphic` sub-`Table`s composes transforms and opacity.
132132
fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) -> Table<T> {
133-
fn compose_alpha_blending(parent: AlphaBlending, child: AlphaBlending) -> AlphaBlending {
134-
AlphaBlending {
135-
blend_mode: child.blend_mode,
136-
opacity: parent.opacity * child.opacity,
137-
fill: child.fill,
138-
clip: child.clip,
139-
}
140-
}
141-
142133
fn flatten_recursive<T>(output: &mut Table<T>, current_graphic_table: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) {
143134
for current_graphic_row in current_graphic_table.into_iter() {
144135
let layer_path: Table<NodeId> = current_graphic_row.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
145136
let current_transform: DAffine2 = current_graphic_row.attribute_cloned_or_default(ATTR_TRANSFORM);
146-
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
137+
let current_opacity: f64 = current_graphic_row.attribute_cloned_or(ATTR_OPACITY, 1.);
138+
let current_fill: f64 = current_graphic_row.attribute_cloned_or(ATTR_OPACITY_FILL, 1.);
147139

148140
match current_graphic_row.into_element() {
149-
// Recurse into nested `Table<Graphic>` items, composing the parent's transform onto each child
141+
// Compose the parent's transform, opacity, and fill onto each child row
150142
Graphic::Graphic(mut sub_table) => {
151-
for index in 0..sub_table.len() {
152-
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
153-
let child_alpha_blending: AlphaBlending = sub_table.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
143+
// Identity default means a missing column still composes correctly
144+
for v in sub_table.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
145+
*v = current_transform * *v;
146+
}
154147

155-
sub_table.set_attribute(ATTR_TRANSFORM, index, current_transform * child_transform);
156-
sub_table.set_attribute(ATTR_ALPHA_BLENDING, index, compose_alpha_blending(current_alpha_blending, child_alpha_blending));
148+
// f64 defaults to 0, but opacity/fill default to 1, so missing columns must be set rather than multiplied
149+
if let Some(values) = sub_table.iter_attribute_values_mut::<f64>(ATTR_OPACITY) {
150+
for v in values {
151+
*v *= current_opacity;
152+
}
153+
} else {
154+
for v in sub_table.iter_attribute_values_mut_or_default::<f64>(ATTR_OPACITY) {
155+
*v = current_opacity;
156+
}
157+
}
158+
if let Some(values) = sub_table.iter_attribute_values_mut::<f64>(ATTR_OPACITY_FILL) {
159+
for v in values {
160+
*v *= current_fill;
161+
}
162+
} else {
163+
for v in sub_table.iter_attribute_values_mut_or_default::<f64>(ATTR_OPACITY_FILL) {
164+
*v = current_fill;
165+
}
157166
}
158167

159168
flatten_recursive(output, sub_table, extract_variant);
160169
}
161-
// Try to extract the target variant; if it matches, push its items with composed transform and opacity
170+
// Extract the target variant and push its items with composed transform, opacity, and fill
162171
other => {
163172
if let Some(typed_table) = extract_variant(other) {
164-
for row in typed_table.into_iter() {
165-
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
166-
let row_alpha_blending: AlphaBlending = row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
167-
let (element, mut attributes) = row.into_parts();
173+
for mut item in typed_table.into_iter() {
174+
let row_transform: DAffine2 = item.attribute_cloned_or_default(ATTR_TRANSFORM);
175+
let row_opacity: f64 = item.attribute_cloned_or(ATTR_OPACITY, 1.);
176+
let row_fill: f64 = item.attribute_cloned_or(ATTR_OPACITY_FILL, 1.);
168177

169-
attributes.insert(ATTR_TRANSFORM, current_transform * row_transform);
170-
attributes.insert(ATTR_ALPHA_BLENDING, compose_alpha_blending(current_alpha_blending, row_alpha_blending));
171-
attributes.insert(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
178+
item.set_attribute(ATTR_TRANSFORM, current_transform * row_transform);
179+
item.set_attribute(ATTR_OPACITY, current_opacity * row_opacity);
180+
item.set_attribute(ATTR_OPACITY_FILL, current_fill * row_fill);
181+
item.set_attribute(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
172182

173-
output.push(TableRow::from_parts(element, attributes));
183+
output.push(item);
174184
}
175185
}
176186
}
@@ -321,8 +331,9 @@ impl Graphic {
321331

322332
pub fn had_clip_enabled(&self) -> bool {
323333
fn all_clipped<T>(table: &Table<T>) -> bool {
324-
table.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING).all(|a| a.clip)
334+
table.iter_attribute_values_or_default::<bool>(ATTR_CLIPPING_MASK).all(|clip| clip)
325335
}
336+
326337
match self {
327338
Graphic::Vector(table) => all_clipped(table),
328339
Graphic::Graphic(table) => all_clipped(table),
@@ -335,12 +346,11 @@ impl Graphic {
335346

336347
pub fn can_reduce_to_clip_path(&self) -> bool {
337348
match self {
338-
Graphic::Vector(vector) => vector
339-
.iter_element_values()
340-
.zip(vector.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING))
341-
.all(|(element, alpha_blending)| {
342-
(alpha_blending.opacity > 1. - f32::EPSILON) && element.style.fill().is_opaque() && element.style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
343-
}),
349+
Graphic::Vector(vector) => (0..vector.len()).all(|index| {
350+
let Some(element) = vector.element(index) else { return false };
351+
let opacity: f64 = vector.attribute_cloned_or(ATTR_OPACITY, index, 1.);
352+
opacity > 1. - f64::EPSILON && element.style.fill().is_opaque() && element.style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
353+
}),
344354
_ => false,
345355
}
346356
}
@@ -474,12 +484,23 @@ impl<T: Clone> OmitIndex for Table<T> {
474484
pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
475485
use serde::Deserialize;
476486

487+
/// Mirrors the removed `AlphaBlending` struct for legacy document deserialization.
488+
#[derive(Clone, Debug, Default, PartialEq)]
489+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
490+
#[cfg_attr(feature = "serde", serde(default))]
491+
pub struct LegacyAlphaBlending {
492+
pub blend_mode: BlendMode,
493+
pub opacity: f32,
494+
pub fill: f32,
495+
pub clip: bool,
496+
}
497+
477498
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
478499
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
479500
pub struct OldGraphicGroup {
480501
elements: Vec<(Graphic, Option<NodeId>)>,
481502
transform: DAffine2,
482-
alpha_blending: AlphaBlending,
503+
alpha_blending: LegacyAlphaBlending,
483504
}
484505
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
485506
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -502,7 +523,7 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
502523
#[cfg_attr(feature = "serde", serde(alias = "instances", alias = "instance"))]
503524
element: Vec<T>,
504525
transform: Vec<DAffine2>,
505-
alpha_blending: Vec<AlphaBlending>,
526+
alpha_blending: Vec<LegacyAlphaBlending>,
506527
}
507528

508529
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

0 commit comments

Comments
 (0)