mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Rename spread method to gradient spread so it matches the other gradient attribute names (#4404)
* Rename spread method to gradient spread so it matches the other gradient attribute names * Keep the legacy gradient struct's spread method field name matching its on-disk key
This commit is contained in:
committed by
Dennis Kobert
parent
c2f27bf743
commit
51ab692df3
@@ -1161,7 +1161,7 @@ impl<T: CacheHash> CacheHash for List<T> {
|
||||
self.element.cache_hash(state);
|
||||
|
||||
// Hash every attribute attribute (key + values) rather than just the well-known ones, so changes to user-defined keys
|
||||
// (e.g., gradient_type, spread_method) invalidate downstream graph caches as expected
|
||||
// (e.g., gradient_type, gradient_spread) invalidate downstream graph caches as expected
|
||||
for (key, attribute) in &self.attributes.attributes {
|
||||
std::hash::Hash::hash(key.as_str(), state);
|
||||
attribute.cache_hash_dyn(state);
|
||||
|
||||
@@ -36,7 +36,7 @@ pub mod migrations {
|
||||
pub start: DVec2,
|
||||
pub end: DVec2,
|
||||
#[serde(default)]
|
||||
pub spread_method: vector::style::GradientSpreadMethod,
|
||||
pub spread_method: vector::style::GradientSpread,
|
||||
#[serde(default)]
|
||||
pub absolute: bool,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -8,11 +8,11 @@ use core_types::uuid::generate_uuid;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::Graphic;
|
||||
use graphic_types::vector_types::gradient::GradientType;
|
||||
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod};
|
||||
use graphic_types::vector_types::markers::{GradientSpread as GradientSpreadAttr, GradientType as GradientTypeAttr};
|
||||
use graphic_types::vector_types::vector::style::{PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use std::fmt::Write;
|
||||
use vector_types::Gradient;
|
||||
use vector_types::gradient::GradientSpreadMethod;
|
||||
use vector_types::gradient::GradientSpread;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum PaintTarget {
|
||||
@@ -110,7 +110,7 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
|
||||
let Some(stops) = source.element(0) else { return 0 };
|
||||
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(0);
|
||||
let local_gradient_transform: DAffine2 = source.attr::<Transform>(0);
|
||||
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(0);
|
||||
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(0);
|
||||
|
||||
for (position, color, original_midpoint) in stops.interpolated_samples() {
|
||||
stop.push_str("<stop");
|
||||
@@ -149,10 +149,10 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
|
||||
format!(r#" gradientTransform="{gradient_transform}""#)
|
||||
};
|
||||
|
||||
let spread_method = if spread_method == GradientSpreadMethod::Pad {
|
||||
let gradient_spread = if gradient_spread == GradientSpread::Pad {
|
||||
String::new()
|
||||
} else {
|
||||
format!(r#" spreadMethod="{}""#, spread_method.svg_name())
|
||||
format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
|
||||
};
|
||||
|
||||
let gradient_id = generate_uuid();
|
||||
@@ -161,14 +161,14 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
|
||||
GradientType::Linear => {
|
||||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<linearGradient id="{}" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1" y2="0"{spread_method}{gradient_transform}>{}</linearGradient>"#,
|
||||
r#"<linearGradient id="{}" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1" y2="0"{gradient_spread}{gradient_transform}>{}</linearGradient>"#,
|
||||
gradient_id, stop
|
||||
);
|
||||
}
|
||||
GradientType::Radial => {
|
||||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<radialGradient id="{}" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1"{spread_method}{gradient_transform}>{}</radialGradient>"#,
|
||||
r#"<radialGradient id="{}" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1"{gradient_spread}{gradient_transform}>{}</radialGradient>"#,
|
||||
gradient_id, stop
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use graphic_types::graphic::{PaintColumns, PaintOverlay, PaintReach, has_paint,
|
||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
|
||||
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture};
|
||||
use graphic_types::vector_types::gradient::{Gradient, GradientType};
|
||||
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod};
|
||||
use graphic_types::vector_types::markers::{GradientSpread as GradientSpreadAttr, GradientType as GradientTypeAttr};
|
||||
use graphic_types::vector_types::subpath::Subpath;
|
||||
use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint};
|
||||
use graphic_types::vector_types::vector::style::{PaintOrder, RenderMode, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
@@ -44,7 +44,7 @@ use std::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use text_nodes::markers::{Font, TextAlign};
|
||||
use vector_types::gradient::GradientSpreadMethod;
|
||||
use vector_types::gradient::GradientSpread;
|
||||
use vector_types::markers::EditorClickTarget;
|
||||
use vello::*;
|
||||
|
||||
@@ -430,7 +430,7 @@ fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list
|
||||
|
||||
let gradient_type: GradientType = gradient_list.attr::<GradientTypeAttr>(0);
|
||||
let gradient_transform: DAffine2 = gradient_list.attr::<Transform>(0);
|
||||
let spread_method: GradientSpreadMethod = gradient_list.attr::<SpreadMethod>(0);
|
||||
let gradient_spread: GradientSpread = gradient_list.attr::<GradientSpreadAttr>(0);
|
||||
|
||||
let peniko_stops = peniko_color_stops(stops);
|
||||
|
||||
@@ -452,10 +452,10 @@ fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list
|
||||
}
|
||||
.into(),
|
||||
},
|
||||
extend: match spread_method {
|
||||
GradientSpreadMethod::Pad => peniko::Extend::Pad,
|
||||
GradientSpreadMethod::Reflect => peniko::Extend::Reflect,
|
||||
GradientSpreadMethod::Repeat => peniko::Extend::Repeat,
|
||||
extend: match gradient_spread {
|
||||
GradientSpread::Pad => peniko::Extend::Pad,
|
||||
GradientSpread::Reflect => peniko::Extend::Reflect,
|
||||
GradientSpread::Repeat => peniko::Extend::Repeat,
|
||||
},
|
||||
stops: peniko_stops,
|
||||
interpolation_alpha_space: peniko::InterpolationAlphaSpace::Premultiplied,
|
||||
@@ -2327,7 +2327,7 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
|
||||
let blend_mode: BlendMode = source.attr::<BlendModeAttr>(index);
|
||||
let opacity_attr: f64 = source.attr::<Opacity>(index);
|
||||
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
|
||||
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(index);
|
||||
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
|
||||
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index);
|
||||
let tag = if thumbnail_rect.is_some() { "rect" } else { "polyline" };
|
||||
render.leaf_tag(tag, |attributes| {
|
||||
@@ -2366,10 +2366,10 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
|
||||
};
|
||||
|
||||
let gradient_id = generate_uuid();
|
||||
let spread_method_attribute = if spread_method == GradientSpreadMethod::Pad {
|
||||
let spread_method_attribute = if gradient_spread == GradientSpread::Pad {
|
||||
String::new()
|
||||
} else {
|
||||
format!(r#" spreadMethod="{}""#, spread_method.svg_name())
|
||||
format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
|
||||
};
|
||||
|
||||
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied
|
||||
@@ -2411,7 +2411,7 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
|
||||
|
||||
for index in 0..source.lane_count() {
|
||||
let Some(gradient) = source.element(index) else { continue };
|
||||
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(index);
|
||||
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
|
||||
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index);
|
||||
let transform: DAffine2 = source.attr::<Transform>(index);
|
||||
let blend_mode_attr: BlendMode = source.attr::<BlendModeAttr>(index);
|
||||
@@ -2424,10 +2424,10 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
|
||||
|
||||
let stops = peniko_color_stops(gradient);
|
||||
|
||||
let extend = match spread_method {
|
||||
GradientSpreadMethod::Pad => peniko::Extend::Pad,
|
||||
GradientSpreadMethod::Reflect => peniko::Extend::Reflect,
|
||||
GradientSpreadMethod::Repeat => peniko::Extend::Repeat,
|
||||
let extend = match gradient_spread {
|
||||
GradientSpread::Pad => peniko::Extend::Pad,
|
||||
GradientSpread::Reflect => peniko::Extend::Reflect,
|
||||
GradientSpread::Repeat => peniko::Extend::Repeat,
|
||||
};
|
||||
|
||||
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ATTR_SPREAD_METHOD;
|
||||
use crate::markers::ATTR_GRADIENT_SPREAD;
|
||||
use core_types::Color;
|
||||
use core_types::color::SRGBA8;
|
||||
use core_types::list::{ATTR_MIDPOINT, ATTR_POSITION, Item, List};
|
||||
@@ -103,9 +103,9 @@ impl GradientStops<SRGBA8> {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GradientRamp<C = Color> {
|
||||
pub stops: GradientStops<C>,
|
||||
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "GradientSpreadMethod::is_default"))]
|
||||
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "GradientSpread::is_default"))]
|
||||
#[cfg_attr(feature = "wasm", tsify(optional))]
|
||||
pub spread_method: GradientSpreadMethod,
|
||||
pub gradient_spread: GradientSpread,
|
||||
}
|
||||
|
||||
unsafe impl<C: dyn_any::StaticTypeSized> dyn_any::StaticType for GradientRamp<C> {
|
||||
@@ -116,7 +116,7 @@ impl<C> From<GradientStops<C>> for GradientRamp<C> {
|
||||
fn from(stops: GradientStops<C>) -> Self {
|
||||
Self {
|
||||
stops,
|
||||
spread_method: Default::default(),
|
||||
gradient_spread: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ impl From<&Gradient> for GradientRamp {
|
||||
fn from(gradient: &Gradient) -> Self {
|
||||
Self {
|
||||
stops: gradient.into(),
|
||||
spread_method: Default::default(),
|
||||
gradient_spread: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,8 +153,8 @@ impl From<&GradientRamp> for Gradient {
|
||||
impl From<GradientRamp> for Item<Gradient> {
|
||||
fn from(ramp: GradientRamp) -> Self {
|
||||
let mut item = Item::new_from_element(Gradient::from(ramp.stops));
|
||||
if !ramp.spread_method.is_default() {
|
||||
item.set_attribute(ATTR_SPREAD_METHOD, ramp.spread_method);
|
||||
if !ramp.gradient_spread.is_default() {
|
||||
item.set_attribute(ATTR_GRADIENT_SPREAD, ramp.gradient_spread);
|
||||
}
|
||||
item
|
||||
}
|
||||
@@ -164,7 +164,7 @@ impl From<&Item<Gradient>> for GradientRamp {
|
||||
fn from(item: &Item<Gradient>) -> Self {
|
||||
Self {
|
||||
stops: item.element().into(),
|
||||
spread_method: item.attribute_cloned_or_default(ATTR_SPREAD_METHOD),
|
||||
gradient_spread: item.attribute_cloned_or_default(ATTR_GRADIENT_SPREAD),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ impl From<&GradientRamp> for GradientRamp<SRGBA8> {
|
||||
fn from(ramp: &GradientRamp) -> Self {
|
||||
Self {
|
||||
stops: ramp.into(),
|
||||
spread_method: ramp.spread_method,
|
||||
gradient_spread: ramp.gradient_spread,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ impl From<&Gradient> for GradientRamp<SRGBA8> {
|
||||
fn from(gradient: &Gradient) -> Self {
|
||||
Self {
|
||||
stops: gradient.into(),
|
||||
spread_method: Default::default(),
|
||||
gradient_spread: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ impl From<&Gradient> for GradientRamp<SRGBA8> {
|
||||
impl From<&GradientRamp<SRGBA8>> for GradientRamp {
|
||||
fn from(ramp: &GradientRamp<SRGBA8>) -> Self {
|
||||
Self {
|
||||
spread_method: ramp.spread_method,
|
||||
gradient_spread: ramp.gradient_spread,
|
||||
..Self::from(&ramp.stops)
|
||||
}
|
||||
}
|
||||
@@ -601,12 +601,12 @@ impl Gradient {
|
||||
stops
|
||||
}
|
||||
|
||||
/// Samples the gradient's color at `t`. Given a `t` outside the 0 to 1 range, the `spread_method` determines how the gradient extends.
|
||||
pub fn evaluate(&self, t: f64, spread_method: GradientSpreadMethod) -> Color {
|
||||
let t = match spread_method {
|
||||
GradientSpreadMethod::Pad => t.clamp(0., 1.),
|
||||
GradientSpreadMethod::Repeat => t.rem_euclid(1.),
|
||||
GradientSpreadMethod::Reflect => {
|
||||
/// Samples the gradient's color at `t`. Given a `t` outside the 0 to 1 range, the `gradient_spread` determines how the gradient extends.
|
||||
pub fn evaluate(&self, t: f64, gradient_spread: GradientSpread) -> Color {
|
||||
let t = match gradient_spread {
|
||||
GradientSpread::Pad => t.clamp(0., 1.),
|
||||
GradientSpread::Repeat => t.rem_euclid(1.),
|
||||
GradientSpread::Reflect => {
|
||||
let cycle = t.rem_euclid(2.);
|
||||
if cycle > 1. { 2. - cycle } else { cycle }
|
||||
}
|
||||
@@ -785,7 +785,7 @@ impl Gradient {
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, graphene_hash::CacheHash, DynAny, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum GradientSpreadMethod {
|
||||
pub enum GradientSpread {
|
||||
/// Extends the end colors outward.
|
||||
#[default]
|
||||
Pad,
|
||||
@@ -796,12 +796,12 @@ pub enum GradientSpreadMethod {
|
||||
// TODO: Add a "Clear" variant that returns transparent black outside the gradient's range
|
||||
}
|
||||
|
||||
impl GradientSpreadMethod {
|
||||
impl GradientSpread {
|
||||
pub fn svg_name(&self) -> &'static str {
|
||||
match self {
|
||||
GradientSpreadMethod::Pad => "pad",
|
||||
GradientSpreadMethod::Reflect => "reflect",
|
||||
GradientSpreadMethod::Repeat => "repeat",
|
||||
GradientSpread::Pad => "pad",
|
||||
GradientSpread::Reflect => "reflect",
|
||||
GradientSpread::Repeat => "repeat",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,39 +906,39 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_method_serializes_only_when_not_default() {
|
||||
fn gradient_spread_serializes_only_when_not_default() {
|
||||
let default_spread = GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE]));
|
||||
let json = serde_json::to_string(&default_spread).unwrap();
|
||||
assert!(!json.contains("spread_method"), "the default Pad spread method must not serialize: {json}");
|
||||
assert!(!json.contains("gradient_spread"), "the default Pad gradient spread must not serialize: {json}");
|
||||
assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), default_spread);
|
||||
|
||||
let repeating = GradientRamp {
|
||||
spread_method: GradientSpreadMethod::Repeat,
|
||||
gradient_spread: GradientSpread::Repeat,
|
||||
..default_spread.clone()
|
||||
};
|
||||
let json = serde_json::to_string(&repeating).unwrap();
|
||||
assert!(json.contains(r#""spread_method":"Repeat""#), "a non-default spread method must serialize: {json}");
|
||||
assert!(json.contains(r#""gradient_spread":"Repeat""#), "a non-default gradient spread must serialize: {json}");
|
||||
assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), repeating);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spread_method_round_trips_through_the_item_attribute() {
|
||||
fn gradient_spread_round_trips_through_the_item_attribute() {
|
||||
let ramp = GradientRamp {
|
||||
spread_method: GradientSpreadMethod::Repeat,
|
||||
gradient_spread: GradientSpread::Repeat,
|
||||
..GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE]))
|
||||
};
|
||||
|
||||
let item = Item::<Gradient>::from(ramp.clone());
|
||||
assert_eq!(
|
||||
item.attribute_cloned_or_default::<GradientSpreadMethod>(ATTR_SPREAD_METHOD),
|
||||
GradientSpreadMethod::Repeat,
|
||||
"the runtime item should carry the spread method as its attribute"
|
||||
item.attribute_cloned_or_default::<GradientSpread>(ATTR_GRADIENT_SPREAD),
|
||||
GradientSpread::Repeat,
|
||||
"the runtime item should carry the gradient spread as its attribute"
|
||||
);
|
||||
assert_eq!(GradientRamp::from(&item), ramp);
|
||||
|
||||
let padded = Item::<Gradient>::from(GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE])));
|
||||
assert!(
|
||||
padded.attribute::<GradientSpreadMethod>(ATTR_SPREAD_METHOD).is_none(),
|
||||
padded.attribute::<GradientSpread>(ATTR_GRADIENT_SPREAD).is_none(),
|
||||
"the default Pad must stay absent rather than materialize"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ pub mod vector;
|
||||
|
||||
// Re-export commonly used types at the crate root
|
||||
pub use core_types as gcore;
|
||||
pub use gradient::{Gradient, GradientRamp, GradientSpreadMethod, GradientStop, GradientType};
|
||||
pub use markers::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
|
||||
pub use gradient::{Gradient, GradientRamp, GradientSpread, GradientStop, GradientType};
|
||||
pub use markers::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_SPREAD, ATTR_GRADIENT_TYPE};
|
||||
pub use math::{QuadExt, RectExt};
|
||||
pub use subpath::Subpath;
|
||||
pub use vector::Vector;
|
||||
|
||||
@@ -5,7 +5,7 @@ use core_types::attribute::Attribute;
|
||||
|
||||
core_types::attribute! {
|
||||
/// Gradient's spread behavior past its endpoints (`Pad`, `Reflect`, or `Repeat`).
|
||||
pub SpreadMethod("spread_method"): crate::gradient::GradientSpreadMethod;
|
||||
pub GradientSpread("gradient_spread"): crate::gradient::GradientSpread;
|
||||
/// Gradient's shape (`Linear` or `Radial`).
|
||||
pub GradientType("gradient_type"): crate::gradient::GradientType;
|
||||
/// Optional `Vector` that overrides the item's own geometry for click-target generation.
|
||||
@@ -18,11 +18,11 @@ core_types::attribute! {
|
||||
// The value types a name-generic attribute can name here, so a compile-time
|
||||
// named write or read reaches this crate's enums like any other plain value.
|
||||
core_types::named_value! {
|
||||
for crate::gradient::GradientSpreadMethod;
|
||||
for crate::gradient::GradientSpread;
|
||||
for crate::gradient::GradientType;
|
||||
}
|
||||
|
||||
pub const ATTR_SPREAD_METHOD: &str = SpreadMethod::NAME;
|
||||
pub const ATTR_GRADIENT_SPREAD: &str = GradientSpread::NAME;
|
||||
pub const ATTR_GRADIENT_TYPE: &str = GradientType::NAME;
|
||||
pub const ATTR_EDITOR_CLICK_TARGET: &str = EditorClickTarget::NAME;
|
||||
|
||||
@@ -35,7 +35,7 @@ mod tests {
|
||||
#[test]
|
||||
fn the_census_carries_this_crates_names() {
|
||||
assert_eq!(info("gradient_type").unwrap().value_type, TypeId::of::<crate::gradient::GradientType>());
|
||||
assert_eq!(info("spread_method").unwrap().value_type, TypeId::of::<crate::gradient::GradientSpreadMethod>());
|
||||
assert_eq!(info("gradient_spread").unwrap().value_type, TypeId::of::<crate::gradient::GradientSpread>());
|
||||
assert_eq!(info("editor:click_target").unwrap().value_type, TypeId::of::<Option<&'static crate::Vector>>());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user