mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add support for setting the spread method for gradient fills (#3953)
* Add spread method support for gradients * Add GradientSpreadMethod enum (Pad, Repeat, Reflect) to vector-types * Add radio buttons to gradient tool and fill properties panel * Convert spread method when importing SVGs via usvg * Sync backup gradient input when changing spread method * Table<GradientStops> rendering is not yet updated for spread method * Sync gradient tool options with layer's gradient * Sync gradient_type and spread_method from the selected layer's existing gradient to the tool options bar when switching to the gradient tool * Refactor has_gradient_on_selected_layers to reuse a new get_gradient_on_selected_layer helper * Swap Reflect and Repeat order in UI radio buttons * Fix alignment of the radio buttons in right panel * Fix the position of the radio buttons in the tool * Rename SpreadMethod to SetSpreadMethod * Move default spread method omission logic
This commit is contained in:
@@ -4,6 +4,7 @@ use glam::DAffine2;
|
||||
use graphic_types::vector_types::gradient::{Gradient, GradientType};
|
||||
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use std::fmt::Write;
|
||||
use vector_types::gradient::GradientSpreadMethod;
|
||||
|
||||
pub trait RenderExt {
|
||||
type Output;
|
||||
@@ -47,13 +48,19 @@ impl RenderExt for Gradient {
|
||||
format!(r#" gradientTransform="{gradient_transform}""#)
|
||||
};
|
||||
|
||||
let spread_method = if self.spread_method == GradientSpreadMethod::Pad {
|
||||
String::new()
|
||||
} else {
|
||||
format!(r#" spreadMethod="{}""#, self.spread_method.svg_name())
|
||||
};
|
||||
|
||||
let gradient_id = generate_uuid();
|
||||
|
||||
match self.gradient_type {
|
||||
GradientType::Linear => {
|
||||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<linearGradient id="{}" x1="{}" y1="{}" x2="{}" y2="{}"{gradient_transform}>{}</linearGradient>"#,
|
||||
r#"<linearGradient id="{}" x1="{}" y1="{}" x2="{}" y2="{}"{spread_method}{gradient_transform}>{}</linearGradient>"#,
|
||||
gradient_id, start.x, start.y, end.x, end.y, stop
|
||||
);
|
||||
}
|
||||
@@ -61,7 +68,7 @@ impl RenderExt for Gradient {
|
||||
let radius = (f64::powi(start.x - end.x, 2) + f64::powi(start.y - end.y, 2)).sqrt();
|
||||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<radialGradient id="{}" cx="{}" cy="{}" r="{}"{gradient_transform}>{}</radialGradient>"#,
|
||||
r#"<radialGradient id="{}" cx="{}" cy="{}" r="{}"{spread_method}{gradient_transform}>{}</radialGradient>"#,
|
||||
gradient_id, start.x, start.y, radius, stop
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use std::fmt::Write;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use vector_types::gradient::GradientSpreadMethod;
|
||||
use vello::*;
|
||||
|
||||
/// Cached 16x16 transparency checkerboard image data (two 8x8 cells of #ffffff and #cccccc).
|
||||
@@ -1159,6 +1160,11 @@ impl Render for Table<Vector> {
|
||||
.into()
|
||||
}
|
||||
},
|
||||
extend: match gradient.spread_method {
|
||||
GradientSpreadMethod::Pad => peniko::Extend::Pad,
|
||||
GradientSpreadMethod::Reflect => peniko::Extend::Reflect,
|
||||
GradientSpreadMethod::Repeat => peniko::Extend::Repeat,
|
||||
},
|
||||
stops,
|
||||
interpolation_alpha_space: peniko::InterpolationAlphaSpace::Premultiplied,
|
||||
..Default::default()
|
||||
|
||||
@@ -334,6 +334,27 @@ impl GradientStops {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize, DynAny, node_macro::ChoiceType)]
|
||||
#[widget(Radio)]
|
||||
pub enum GradientSpreadMethod {
|
||||
#[default]
|
||||
Pad,
|
||||
Reflect,
|
||||
Repeat,
|
||||
}
|
||||
|
||||
impl GradientSpreadMethod {
|
||||
pub fn svg_name(&self) -> &'static str {
|
||||
match self {
|
||||
GradientSpreadMethod::Pad => "pad",
|
||||
GradientSpreadMethod::Reflect => "reflect",
|
||||
GradientSpreadMethod::Repeat => "repeat",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A gradient fill.
|
||||
///
|
||||
/// Contains the start and end points, along with the colors at varying points along the length.
|
||||
@@ -345,6 +366,8 @@ pub struct Gradient {
|
||||
pub gradient_type: GradientType,
|
||||
pub start: DVec2,
|
||||
pub end: DVec2,
|
||||
#[serde(default)]
|
||||
pub spread_method: GradientSpreadMethod,
|
||||
}
|
||||
|
||||
impl Default for Gradient {
|
||||
@@ -354,6 +377,7 @@ impl Default for Gradient {
|
||||
gradient_type: GradientType::Linear,
|
||||
start: DVec2::new(0., 0.5),
|
||||
end: DVec2::new(1., 0.5),
|
||||
spread_method: GradientSpreadMethod::Pad,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,6 +393,7 @@ impl std::hash::Hash for Gradient {
|
||||
.for_each(|x| x.to_bits().hash(state));
|
||||
self.stops.color.iter().for_each(|color| color.hash(state));
|
||||
self.gradient_type.hash(state);
|
||||
self.spread_method.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +412,7 @@ impl std::fmt::Display for Gradient {
|
||||
|
||||
impl Gradient {
|
||||
/// Constructs a new gradient with the colors at 0 and 1 specified.
|
||||
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, gradient_type: GradientType) -> Self {
|
||||
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, gradient_type: GradientType, spread_method: GradientSpreadMethod) -> Self {
|
||||
let stops = GradientStops::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
@@ -401,7 +426,13 @@ impl Gradient {
|
||||
},
|
||||
]);
|
||||
|
||||
Self { start, end, stops, gradient_type }
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
stops,
|
||||
gradient_type,
|
||||
spread_method,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lerp(&self, other: &Self, time: f64) -> Self {
|
||||
@@ -414,8 +445,15 @@ impl Gradient {
|
||||
});
|
||||
let stops = GradientStops::new(stops);
|
||||
let gradient_type = if time < 0.5 { self.gradient_type } else { other.gradient_type };
|
||||
let spread_method = if time < 0.5 { self.spread_method } else { other.spread_method };
|
||||
|
||||
Self { start, end, stops, gradient_type }
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
stops,
|
||||
gradient_type,
|
||||
spread_method,
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a stop into the gradient, the index if successful
|
||||
|
||||
Reference in New Issue
Block a user