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:
YohYamasaki
2026-04-14 10:09:57 +02:00
committed by GitHub
parent da45ab2f87
commit 79d778a535
6 changed files with 232 additions and 14 deletions

View File

@@ -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
);
}

View File

@@ -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()