Deprecate all usages of the Color struct representing gamma space values, fixing round-trip precision bugs (#4149)

* Deprecate all usages of the Color struct representing gamma space values, fixing round-trip precision bugs

* Code review fixes
This commit is contained in:
Keavon Chambers
2026-05-14 22:48:33 -07:00
committed by GitHub
parent 456a7c868d
commit a56746c6bf
67 changed files with 1210 additions and 941 deletions
@@ -7,12 +7,13 @@ use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{Affine2, DAffine2, Vec2};
use graph_craft::document::NodeId;
use graphene_std::blending::BlendMode;
use graphene_std::color::SRGBA8;
use graphene_std::gradient::GradientStops;
use graphene_std::list::List;
use graphene_std::memo::IORecord;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::vector::Vector;
use graphene_std::vector::style::{Fill, FillChoice, GradientSpreadMethod, GradientType};
use graphene_std::vector::style::{Fill, FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType};
use graphene_std::{Artboard, Color, Context, Graphic};
use std::any::Any;
use std::sync::Arc;
@@ -385,11 +386,15 @@ impl TableItemLayout for Vector {
match self.style.fill.clone() {
Fill::None => table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoice::None).disabled(true).menu_direction(Some(MenuDirection::Top)).narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::None)
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]),
Fill::Solid(color) => table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoice::Solid(color))
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(color)))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
@@ -398,7 +403,7 @@ impl TableItemLayout for Vector {
Fill::Gradient(gradient) => {
table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoice::Gradient(gradient.stops))
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(gradient.stops)))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
@@ -423,7 +428,11 @@ impl TableItemLayout for Vector {
let color = if let Some(color) = stroke.color { FillChoice::Solid(color) } else { FillChoice::None };
table_rows.push(vec![
TextLabel::new("Stroke").narrow(true).widget_instance(),
ColorInput::new(color).disabled(true).menu_direction(Some(MenuDirection::Top)).narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::from(&color))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Weight").narrow(true).widget_instance(),
@@ -561,10 +570,10 @@ impl TableItemLayout for Color {
"Color"
}
fn identifier(&self) -> String {
format!("Color (#{})", self.to_gamma_srgb().to_rgba_hex_srgb())
format!("Color (#{})", SRGBA8::from(*self).to_rgba_hex())
}
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
ColorInput::new(FillChoice::Solid(*self))
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(*self)))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
@@ -584,7 +593,7 @@ impl TableItemLayout for GradientStops {
format!("Gradient ({} stops)", self.len())
}
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
ColorInput::new(FillChoice::Gradient(self.clone()))
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(self.clone())))
.menu_direction(Some(MenuDirection::Top))
.disabled(true)
.narrow(true)
@@ -30,7 +30,7 @@ use graphene_std::text_nodes::StringCapitalization;
use graphene_std::transform::{Footprint, ReferencePoint, ScaleType, Transform};
use graphene_std::vector::misc::BooleanOperation;
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
use graphene_std::vector::style::{Fill, FillChoice, GradientSpreadMethod, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
use graphene_std::vector::style::{Fill, FillChoice, FillChoiceUI, GradientSpreadMethod, GradientStops, GradientStopsUI, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
@@ -1164,19 +1164,19 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
match &**tagged_value {
TaggedValue::Color(color) => widgets.push(
color_button
.value(match color {
.value(FillChoiceUI::from(&match color {
Some(color) => FillChoice::Solid(*color),
None => FillChoice::None,
})
.on_update(update_value(|input: &ColorInput| TaggedValue::Color(input.value.as_solid()), node_id, index))
}))
.on_update(update_value(|input: &ColorInput| TaggedValue::Color(input.value.as_solid().map(Color::from)), node_id, index))
.on_commit(commit_value)
.widget_instance(),
),
TaggedValue::Gradient(stops) => widgets.push(
color_button
.value(FillChoice::Gradient(stops.clone()))
.value(FillChoiceUI::from(&FillChoice::Gradient(stops.clone())))
.on_update(update_value(
|input: &ColorInput| TaggedValue::Gradient(input.value.as_gradient().cloned().unwrap_or_default()),
|input: &ColorInput| TaggedValue::Gradient(input.value.as_gradient().map(GradientStops::from).unwrap_or_default()),
node_id,
index,
))
@@ -1417,7 +1417,7 @@ fn build_shared_spectrum_section(node_id: NodeId, context: &mut NodePropertiesCo
// Build the shared spectrum widget (placed on the first non-exposed row)
let spectrum_widget = (!spectrum_markers.is_empty()).then(|| {
SpectrumInput::new(bw_track())
SpectrumInput::new(GradientStopsUI::from(&bw_track()))
.markers(spectrum_markers)
.show_midpoints(false)
.allow_insert(false)
@@ -1591,7 +1591,7 @@ fn spectrum_slider_row(
let position_to_value = move |position: f64| value_min + position * value_range;
row.push(
SpectrumInput::new(track)
SpectrumInput::new(GradientStopsUI::from(&track))
.markers(vec![SpectrumMarker::new(position, 0.5, handle_color)])
.show_midpoints(false)
.allow_insert(false)
@@ -2482,7 +2482,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
widgets_first_row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
widgets_first_row.push(
ColorInput::default()
.value(fill.clone().into())
.value(FillChoiceUI::from(&FillChoice::from(fill.clone())))
.on_update(move |x: &ColorInput| Message::Batched {
messages: Box::new([
match &fill2 {
@@ -2508,7 +2508,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
NodeGraphMessage::SetInputValue {
node_id,
input_index: FillInput::<Color>::INDEX,
value: TaggedValue::Fill(x.value.to_fill(fill2.as_gradient())),
value: TaggedValue::Fill(FillChoice::from(&x.value).to_fill(fill2.as_gradient())),
}
.into(),
]),
@@ -3,9 +3,9 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex
use crate::messages::portfolio::document::utility_types::misc::{GridSnapping, GridType};
use crate::messages::prelude::*;
use glam::DVec2;
use graphene_std::raster::color::Color;
use graphene_std::color::SRGBA8;
use graphene_std::renderer::Quad;
use graphene_std::vector::style::FillChoice;
use graphene_std::vector::style::FillChoiceUI;
fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
let origin = document.snapping_state.grid.origin;
@@ -274,12 +274,12 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
Separator::new(SeparatorStyle::Related).widget_instance(),
]);
color_widgets.push(
ColorInput::new(FillChoice::Solid(Color::from_hex_str(&grid.color).unwrap_or(Color::BLACK)))
ColorInput::new(FillChoiceUI::Solid(SRGBA8::from_hex_str(&grid.color).unwrap_or(SRGBA8::BLACK)))
.tooltip_label("Grid Display Color")
.allow_none(false)
.on_update(update_val::<ColorInput, _>(grid, |grid, color| {
if let Some(color) = color.value.as_solid() {
grid.color = format!("#{}", color.to_rgba_hex_srgb_from_gamma());
if let Some(srgba) = color.value.as_solid() {
grid.color = srgba.to_css_hex();
}
}))
.widget_instance(),
@@ -999,13 +999,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
});
// Add default fill and stroke to the layer
let fill_color = Color::WHITE;
let stroke_color = Color::BLACK;
let fill = graphene_std::vector::style::Fill::solid(fill_color.to_gamma_srgb());
let fill = graphene_std::vector::style::Fill::solid(Color::WHITE);
responses.add(GraphOperationMessage::FillSet { layer, fill });
let stroke = graphene_std::vector::style::Stroke::new(Some(stroke_color.to_gamma_srgb()), DEFAULT_STROKE_WIDTH);
let stroke = graphene_std::vector::style::Stroke::new(Some(Color::BLACK), DEFAULT_STROKE_WIDTH);
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
// Create new point ids and add those into the existing Vector path
@@ -34,6 +34,7 @@ impl FontCatalog {
}
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(from_wasm_abi))]
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct FontCatalogFamily {
/// The font family name.
@@ -55,6 +56,7 @@ impl FontCatalogFamily {
}
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct FontCatalogStyle {
pub weight: u32,
@@ -88,7 +90,7 @@ impl FontCatalogStyle {
}
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(from_wasm_abi))]
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum PanelType {
Welcome,
@@ -124,7 +126,7 @@ impl PanelType {
pub struct PanelGroupId(pub u64);
/// Which edge of a panel group to split on when docking a dragged panel.
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(from_wasm_abi))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum DockingSplitDirection {
Left,