mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Migrate legacy gradients via an isolated measurement pre-pass (#4268)
* Migrate legacy gradients via an isolated measurement pre-pass Convert legacy bounding-box-relative Fill::Gradient values to absolute space on document load by measuring each fill's evaluated geometry, rather than walking each layer's primary flow. The pre-pass scans the whole root network so fills on secondary branches (e.g. a Copy to Points instance) and fills in hidden, disabled, or orphaned branches are all found, redirecting the document export to each fill in turn to force its branch to evaluate and reading its geometry back from the inspect result. With every legacy gradient converted before any render, the legacy bounding-box render path is removed: the ATTR_GRADIENT_LEGACY marker, the renderer's legacy brush branch, and fill_to_graphic_list's bbox bake all go away. Gradients nested inside subgraph node networks are out of scope and warned about. * Address review: harden gradient migration against stalls and document switches - Advance the queue on dispatch early returns (missing export or channel send error) so a failure can't leave the migration permanently stuck with the document unable to render. - Tag the migration with its DocumentId: cancel-and-restart when a different document's migration is requested, and only apply a measurement to the active document's in-progress migration, so switching documents mid-migration can't stall or cross-apply. * Remove nested network non-migration warning * Cleanup * Also add migrations for Fill node backup colors * Apply migrations to the demo art
This commit is contained in:
@@ -25,8 +25,8 @@ pub use graphene_hash;
|
||||
pub use graphene_hash::CacheHash;
|
||||
pub use list::{
|
||||
ATTR_BACKGROUND, ATTR_BLEND_MODE, ATTR_CLIP, ATTR_CLIPPING_MASK, ATTR_DIMENSIONS, ATTR_EDITOR_CLICK_TARGET, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_EDITOR_TEXT_FRAME, ATTR_END,
|
||||
ATTR_FONT, ATTR_FONT_SIZE, ATTR_GRADIENT_LEGACY, ATTR_GRADIENT_TYPE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_LOCATION, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_NAME,
|
||||
ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD, ATTR_START, ATTR_TEXT_ALIGN, ATTR_TRANSFORM, ATTR_TYPE,
|
||||
ATTR_FONT, ATTR_FONT_SIZE, ATTR_GRADIENT_TYPE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_LOCATION, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_NAME, ATTR_OPACITY, ATTR_OPACITY_FILL,
|
||||
ATTR_SPREAD_METHOD, ATTR_START, ATTR_TEXT_ALIGN, ATTR_TRANSFORM, ATTR_TYPE,
|
||||
};
|
||||
pub use memo::MemoHash;
|
||||
pub use no_std_types::AsU32;
|
||||
|
||||
@@ -58,10 +58,6 @@ pub const ATTR_CLIP: &str = "clip";
|
||||
pub const ATTR_SPREAD_METHOD: &str = "spread_method";
|
||||
/// Gradient's `GradientType` (`Linear` or `Radial`).
|
||||
pub const ATTR_GRADIENT_TYPE: &str = "gradient_type";
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
/// `bool` runtime marker (never serialized) flagging a gradient that came from the legacy bounding-box-relative `Fill::Gradient`,
|
||||
/// so the renderer reproduces the pre-#4241 positioning instead of the new absolute path.
|
||||
pub const ATTR_GRADIENT_LEGACY: &str = "gradient_legacy";
|
||||
/// Vector graphics object's filled area paint, of type List<T> where T is any graphic type.
|
||||
pub const ATTR_FILL: &str = "fill";
|
||||
/// Vector graphics object's stroke paint, of type List<T> where T is any graphic type.
|
||||
|
||||
@@ -4,7 +4,7 @@ use core_types::list::{ATTR_FILL, ATTR_STROKE, Item, List};
|
||||
use core_types::ops::{FromAnchorPosition, ListConvert};
|
||||
use core_types::render_complexity::RenderComplexity;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_GRADIENT_LEGACY, ATTR_GRADIENT_TYPE, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color};
|
||||
use core_types::{ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_GRADIENT_TYPE, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
@@ -193,26 +193,15 @@ fn flatten_graphic_list<T>(content: List<Graphic>, extract_variant: fn(Graphic)
|
||||
|
||||
/// Converts a `Fill` enum into the `List<Graphic>` representation used as paint storage.
|
||||
/// TODO: Remove once all fill paint sources flow through `List<Graphic>` directly without going through the `Fill` enum.
|
||||
pub fn fill_to_graphic_list(fill: &Fill, bounding_box_transform: DAffine2) -> Option<List<Graphic>> {
|
||||
pub fn fill_to_graphic_list(fill: &Fill) -> Option<List<Graphic>> {
|
||||
match fill {
|
||||
Fill::None => None,
|
||||
Fill::Solid(color) => Some(List::new_from_element((*color).into())),
|
||||
Fill::Gradient(gradient) => {
|
||||
let gradient_transform = gradient.transform * gradient.to_transform();
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
// Absolute gradients carry their effective frame into the new pipeline; legacy bounding-box gradients bake the bbox
|
||||
// in and flag the legacy render path until the deferred migration converts them.
|
||||
let (transform, legacy) = if gradient.absolute {
|
||||
(gradient_transform, false)
|
||||
} else {
|
||||
(bounding_box_transform * gradient_transform, true)
|
||||
};
|
||||
let gradient_item = Item::new_from_element(gradient.stops.clone())
|
||||
.with_attribute(ATTR_TRANSFORM, transform)
|
||||
.with_attribute(ATTR_TRANSFORM, gradient.transform * gradient.to_transform())
|
||||
.with_attribute(ATTR_GRADIENT_TYPE, gradient.gradient_type)
|
||||
.with_attribute(ATTR_SPREAD_METHOD, gradient.spread_method)
|
||||
.with_attribute(ATTR_GRADIENT_LEGACY, legacy);
|
||||
.with_attribute(ATTR_SPREAD_METHOD, gradient.spread_method);
|
||||
let gradient_list = List::new_from_item(gradient_item);
|
||||
|
||||
Some(List::new_from_element(Graphic::Gradient(gradient_list)))
|
||||
@@ -257,9 +246,7 @@ pub fn has_paint_at(list: &List<Vector>, index: usize, attribute: &str) -> bool
|
||||
pub fn fill_graphic_list_at(list: &List<Vector>, index: usize) -> Option<Cow<'_, List<Graphic>>> {
|
||||
graphic_list_at(list, index, ATTR_FILL).or_else(|| {
|
||||
let vector = list.element(index)?;
|
||||
let bounds = vector.nonzero_bounding_box();
|
||||
let bounding_box_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||
fill_to_graphic_list(vector.style.fill(), bounding_box_transform).map(Cow::Owned)
|
||||
fill_to_graphic_list(vector.style.fill()).map(Cow::Owned)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ use core_types::transform::Footprint;
|
||||
use core_types::uuid::{NodeId, generate_uuid};
|
||||
use core_types::{
|
||||
ATTR_BACKGROUND, ATTR_BLEND_MODE, ATTR_CLIP, ATTR_CLIPPING_MASK, ATTR_DIMENSIONS, ATTR_EDITOR_CLICK_TARGET, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_EDITOR_TEXT_FRAME, ATTR_FONT,
|
||||
ATTR_FONT_SIZE, ATTR_GRADIENT_LEGACY, ATTR_GRADIENT_TYPE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_LOCATION, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_OPACITY, ATTR_OPACITY_FILL,
|
||||
ATTR_SPREAD_METHOD, ATTR_TEXT_ALIGN, ATTR_TRANSFORM,
|
||||
ATTR_FONT_SIZE, ATTR_GRADIENT_TYPE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_LOCATION, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD,
|
||||
ATTR_TEXT_ALIGN, ATTR_TRANSFORM,
|
||||
};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
@@ -396,14 +396,12 @@ pub(crate) fn gradient_placement(transform: DAffine2, gradient_type: GradientTyp
|
||||
}
|
||||
}
|
||||
|
||||
fn create_peniko_gradient_brush(gradient_list: &List<GradientStops>, parent_transform: &DAffine2, multiplied_transform: &DAffine2) -> Option<(peniko::Brush, DAffine2)> {
|
||||
fn create_peniko_gradient_brush(gradient_list: &List<GradientStops>, multiplied_transform: &DAffine2) -> Option<(peniko::Brush, DAffine2)> {
|
||||
let stops = gradient_list.element(0)?;
|
||||
|
||||
let gradient_type: GradientType = gradient_list.attribute_cloned_or_default(ATTR_GRADIENT_TYPE, 0);
|
||||
let gradient_transform: DAffine2 = gradient_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
let spread_method: GradientSpreadMethod = gradient_list.attribute_cloned_or_default(ATTR_SPREAD_METHOD, 0);
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
let legacy_bounding_box: bool = gradient_list.attribute_cloned_or_default(ATTR_GRADIENT_LEGACY, 0);
|
||||
|
||||
let mut peniko_stops = peniko::ColorStops::new();
|
||||
for (position, color, _) in stops.interpolated_samples() {
|
||||
@@ -413,20 +411,8 @@ fn create_peniko_gradient_brush(gradient_list: &List<GradientStops>, parent_tran
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
// Legacy bounding-box gradients reproduce the pre-#4241 renderer: literal endpoints in the layer's own space, with the
|
||||
// parent transform applied as the brush so its shear bends the bands. New gradients use the unit gradient placed by the desheared frame.
|
||||
let (start, end, gradient_to_device) = if legacy_bounding_box {
|
||||
let inverse_parent_transform = if transform_is_invertible(*parent_transform) {
|
||||
parent_transform.inverse()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let mod_points = inverse_parent_transform * *multiplied_transform * gradient_transform;
|
||||
(mod_points.transform_point2(DVec2::ZERO), mod_points.transform_point2(DVec2::X), *parent_transform)
|
||||
} else {
|
||||
(DVec2::ZERO, DVec2::X, gradient_placement(multiplied_transform * gradient_transform, gradient_type))
|
||||
};
|
||||
// The unit gradient is placed by the desheared frame so a non-uniform transform produces the intended ellipse
|
||||
let (start, end, gradient_to_device) = (DVec2::ZERO, DVec2::X, gradient_placement(multiplied_transform * gradient_transform, gradient_type));
|
||||
|
||||
let brush = peniko::Brush::Gradient(peniko::Gradient {
|
||||
kind: match gradient_type {
|
||||
@@ -1372,7 +1358,7 @@ impl Render for List<Vector> {
|
||||
scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, None, path);
|
||||
}
|
||||
Graphic::Gradient(list) => {
|
||||
let Some((brush, gradient_to_device)) = create_peniko_gradient_brush(list, &parent_transform, &multiplied_transform) else {
|
||||
let Some((brush, gradient_to_device)) = create_peniko_gradient_brush(list, &multiplied_transform) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -1454,7 +1440,7 @@ impl Render for List<Vector> {
|
||||
scene.stroke(&stroke, kurbo::Affine::new(element_transform.to_cols_array()), &brush, None, &path);
|
||||
}
|
||||
Graphic::Gradient(list) => {
|
||||
let Some((brush, gradient_to_device)) = create_peniko_gradient_brush(list, &parent_transform, &multiplied_transform) else {
|
||||
let Some((brush, gradient_to_device)) = create_peniko_gradient_brush(list, &multiplied_transform) else {
|
||||
continue;
|
||||
};
|
||||
let inverse_element_transform = if transform_is_invertible(element_transform) {
|
||||
|
||||
Reference in New Issue
Block a user