mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Fix Vello rendering incompatibility with its gradients using premultiplied alpha instead of SVG's straight (#4419)
* Upgrade Vello to a git main revision that honors the brush's interpolation alpha space * Interpolate Vello gradients with straight alpha, matching the SVG renderer * Draw the gradient picker strip and fill swatches as SVG so transparency previews with straight alpha * Keep gradient stop handles opaque so a transparent stop's RGB stays visible * Paint a stopless gradient's picker strip and swatch black rather than transparent
This commit is contained in:
@@ -539,9 +539,14 @@ fn populate_computed_display_fields(layout: &mut Layout) {
|
||||
hue_direction: spectrum_input.track_hue_direction,
|
||||
interpolation: spectrum_input.track_interpolation,
|
||||
};
|
||||
spectrum_input.track_css = spectrum_input.track.to_css_linear_gradient(settings);
|
||||
let track_gradient = graphene_std::vector::style::Gradient::from(&spectrum_input.track);
|
||||
spectrum_input.track_samples = track_gradient
|
||||
.interpolated_samples_or_black(settings)
|
||||
.into_iter()
|
||||
.map(|(position, color, _)| SpectrumSample::new(position, color))
|
||||
.collect();
|
||||
// The end caps sample the track's boundary colors, which a cyclic wrap makes the wrapped interval's boundary-crossing color rather than the outermost stops'
|
||||
let track_evaluator = graphene_std::vector::style::Gradient::from(&spectrum_input.track).evaluator(settings);
|
||||
let track_evaluator = track_gradient.evaluator(settings);
|
||||
let cap = |t: f64| {
|
||||
let color = track_evaluator.evaluate(t);
|
||||
SRGBA8::from(color).to_css_hex()
|
||||
|
||||
@@ -586,22 +586,22 @@ pub struct SpectrumInput {
|
||||
/// The colored gradient drawn behind the markers (display-only, caller-owned).
|
||||
#[widget_builder(constructor)]
|
||||
pub track: GradientStops<SRGBA8>,
|
||||
/// The color space the track's stops interpolate in, used to compute `track_css`. Not sent to the frontend.
|
||||
/// The color space the track's stops interpolate in, used to bake `track_samples`. Not sent to the frontend.
|
||||
#[serde(skip)]
|
||||
pub track_space: GradientSpace,
|
||||
/// Whether the track's stops wrap as a cycle, used to compute `track_css` and by the frontend to draw the wrapped interval's midpoint diamond.
|
||||
/// Whether the track's stops wrap as a cycle, used to bake `track_samples` and by the frontend to draw the wrapped interval's midpoint diamond.
|
||||
#[serde(rename = "trackCyclic")]
|
||||
pub track_cyclic: bool,
|
||||
/// The hue direction the track's stops interpolate with in a polar space, used to compute `track_css`. Not sent to the frontend.
|
||||
/// The hue direction the track's stops interpolate with in a polar space, used to bake `track_samples`. Not sent to the frontend.
|
||||
#[serde(skip)]
|
||||
pub track_hue_direction: GradientHueDirection,
|
||||
/// The path the track's stops interpolate along, used to compute `track_css` and by the frontend to suppress the midpoint diamonds when stepped.
|
||||
/// The path the track's stops interpolate along, used to bake `track_samples` and by the frontend to suppress the midpoint diamonds when stepped.
|
||||
#[serde(rename = "trackInterpolation")]
|
||||
pub track_interpolation: GradientInterpolation,
|
||||
/// CSS `linear-gradient(...)` string for the track strip's `background-image`. Auto-populated from `track` at layout-send time.
|
||||
#[serde(rename = "trackCSS")]
|
||||
/// Straight-alpha samples the frontend draws as the stops of an SVG gradient filling the track strip. Auto-populated from `track` at layout-send time.
|
||||
#[serde(rename = "trackSamples")]
|
||||
#[widget_builder(skip)]
|
||||
pub track_css: String,
|
||||
pub track_samples: Vec<SpectrumSample>,
|
||||
/// Hex string for the track strip's leftmost solid-color end-cap. Auto-populated by evaluating `track` at position 0.
|
||||
#[serde(rename = "trackStartCSS")]
|
||||
#[widget_builder(skip)]
|
||||
@@ -650,18 +650,40 @@ pub struct SpectrumMarker {
|
||||
/// Position (0..1) of the midpoint between this marker and the next, used only if `show_midpoints` is true.
|
||||
/// The last marker's value controls the wrapped interval when `track_cyclic` is set, and is otherwise ignored.
|
||||
midpoint: f64,
|
||||
/// CSS color string for the marker handle's fill. Set via `SpectrumMarker::new` from a linear [`Color`].
|
||||
/// CSS color string for the marker handle's fill. Set via `SpectrumMarker::new` from a linear [`Color`],
|
||||
/// discarding any transparency so the handle always shows the RGB that steers the interpolation.
|
||||
#[serde(rename = "handleColorCSS")]
|
||||
handle_color_css: String,
|
||||
}
|
||||
|
||||
impl SpectrumMarker {
|
||||
pub fn new(position: f64, midpoint: f64, handle_color: Color) -> Self {
|
||||
let handle_color_css = SRGBA8::from(handle_color).to_css_hex();
|
||||
let handle_color_css = format!("#{}", SRGBA8::from(handle_color).to_rgb_hex());
|
||||
Self { position, midpoint, handle_color_css }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SpectrumSample {
|
||||
/// Position (0..1) of the sample along the spectrum track, drawn as the SVG stop's `offset`.
|
||||
position: f64,
|
||||
/// `#rrggbb` hex of the sample's color, drawn as the SVG stop's `stop-color`.
|
||||
color: String,
|
||||
/// Straight alpha (0..1) of the sample, drawn as the SVG stop's `stop-opacity`.
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl SpectrumSample {
|
||||
pub fn new(position: f64, color: Color) -> Self {
|
||||
Self {
|
||||
position,
|
||||
color: format!("#{}", SRGBA8::from(color).to_rgb_hex()),
|
||||
alpha: color.a(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SpectrumInputUpdate {
|
||||
|
||||
@@ -963,12 +963,12 @@ impl Fsm for GradientToolFsmState {
|
||||
|
||||
let (start, end) = (unit_to_viewport.transform_point2(DVec2::ZERO), unit_to_viewport.transform_point2(DVec2::X));
|
||||
|
||||
fn color_to_hex(color: graphene_std::Color) -> String {
|
||||
SRGBA8::from(color).to_css_hex()
|
||||
fn color_to_opaque_hex(color: graphene_std::Color) -> String {
|
||||
format!("#{}", SRGBA8::from(color).to_rgb_hex())
|
||||
}
|
||||
|
||||
let start_hex = gradient.color(0).map(color_to_hex).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
|
||||
let end_hex = gradient.color(gradient.len().saturating_sub(1)).map(color_to_hex).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
|
||||
let start_hex = gradient.color(0).map(color_to_opaque_hex).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
|
||||
let end_hex = gradient.color(gradient.len().saturating_sub(1)).map(color_to_opaque_hex).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
|
||||
|
||||
// Check if the first/last stops are at position ~0/~1 (rendered as the endpoint dots rather than as separate stops)
|
||||
let settings = appearance.settings;
|
||||
@@ -1027,7 +1027,7 @@ impl Fsm for GradientToolFsmState {
|
||||
StopId::End => overlay_context.gradient_color_stop(end, emphasis, &end_hex, !last_at_end),
|
||||
StopId::Middle(i) => {
|
||||
if let Some(color) = gradient.color(i) {
|
||||
overlay_context.gradient_color_stop(start.lerp(end, gradient.position(i, gradient_cyclic)), emphasis, &color_to_hex(color), false);
|
||||
overlay_context.gradient_color_stop(start.lerp(end, gradient.position(i, gradient_cyclic)), emphasis, &color_to_opaque_hex(color), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user