mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 07:08:11 +08:00
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:
@@ -16,14 +16,17 @@ use editor::consts::FILE_EXTENSION;
|
||||
use editor::messages::clipboard::utility_types::ClipboardContentRaw;
|
||||
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
||||
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta};
|
||||
use editor::messages::layout::utility_types::layout_widget::LayoutTarget;
|
||||
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport;
|
||||
use editor::messages::portfolio::utility_types::{DockingSplitDirection, FontCatalog, FontCatalogFamily, PanelGroupId, PanelType};
|
||||
use editor::messages::prelude::*;
|
||||
use editor::messages::tool::tool_messages::tool_prelude::WidgetId;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::graphene_hash::CacheHashWrapper;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI};
|
||||
use serde::Serialize;
|
||||
use serde_wasm_bindgen::{self, from_value};
|
||||
use std::cell::RefCell;
|
||||
@@ -319,20 +322,20 @@ impl EditorWrapper {
|
||||
|
||||
/// Update the value of a given UI widget, but don't commit it to the history (unless `commit_layout()` is called, which handles that)
|
||||
#[wasm_bindgen(js_name = widgetValueUpdate)]
|
||||
pub fn widget_value_update(&self, layout_target: JsValue, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
pub fn widget_value_update(&self, layout_target: LayoutTarget, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
self.widget_value_update_helper(layout_target, widget_id, value, resend_widget)
|
||||
}
|
||||
|
||||
/// Commit the value of a given UI widget to the history
|
||||
#[wasm_bindgen(js_name = widgetValueCommit)]
|
||||
pub fn widget_value_commit(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
pub fn widget_value_commit(&self, layout_target: LayoutTarget, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
self.widget_value_commit_helper(layout_target, widget_id, value)
|
||||
}
|
||||
|
||||
/// Update the value of a given UI widget, and commit it to the history
|
||||
#[wasm_bindgen(js_name = widgetValueCommitAndUpdate)]
|
||||
pub fn widget_value_commit_and_update(&self, layout_target: JsValue, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
self.widget_value_commit_helper(layout_target.clone(), widget_id, value.clone())?;
|
||||
pub fn widget_value_commit_and_update(&self, layout_target: LayoutTarget, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
self.widget_value_commit_helper(layout_target, widget_id, value.clone())?;
|
||||
self.widget_value_update_helper(layout_target, widget_id, value, resend_widget)?;
|
||||
// Close out a transaction that the widget's `on_commit` opened (if any), so a single click on widgets like the
|
||||
// NumberInput's increment buttons collapses into one history step instead of leaving the transaction in `Modified`
|
||||
@@ -346,34 +349,24 @@ impl EditorWrapper {
|
||||
self.dispatch(DocumentMessage::EndTransaction);
|
||||
}
|
||||
|
||||
pub fn widget_value_update_helper(&self, layout_target: JsValue, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
pub fn widget_value_update_helper(&self, layout_target: LayoutTarget, widget_id: u64, value: JsValue, resend_widget: bool) -> Result<(), JsValue> {
|
||||
let widget_id = WidgetId(widget_id);
|
||||
match (from_value(layout_target), from_value(value)) {
|
||||
(Ok(layout_target), Ok(value)) => {
|
||||
let message = LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
|
||||
if resend_widget {
|
||||
let resend_message = LayoutMessage::ResendActiveWidget { layout_target, widget_id };
|
||||
self.dispatch(resend_message);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
(target, val) => Err(Error::new(&format!("Could not update UI\nDetails:\nTarget: {target:?}\nValue: {val:?}")).into()),
|
||||
let value: serde_json::Value = from_value(value).map_err(|e| Error::new(&format!("Could not update UI: {e}")))?;
|
||||
let message = LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
if resend_widget {
|
||||
let resend_message = LayoutMessage::ResendActiveWidget { layout_target, widget_id };
|
||||
self.dispatch(resend_message);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn widget_value_commit_helper(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
pub fn widget_value_commit_helper(&self, layout_target: LayoutTarget, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
let widget_id = WidgetId(widget_id);
|
||||
match (from_value(layout_target), from_value(value)) {
|
||||
(Ok(layout_target), Ok(value)) => {
|
||||
let message = LayoutMessage::WidgetValueCommit { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
Ok(())
|
||||
}
|
||||
(target, val) => Err(Error::new(&format!("Could not commit UI\nDetails:\nTarget: {target:?}\nValue: {val:?}")).into()),
|
||||
}
|
||||
let value: serde_json::Value = from_value(value).map_err(|e| Error::new(&format!("Could not commit UI: {e}")))?;
|
||||
let message = LayoutMessage::WidgetValueCommit { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = loadPreferences)]
|
||||
@@ -389,14 +382,8 @@ impl EditorWrapper {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = loadPersistedState)]
|
||||
pub fn load_persisted_state(&self, state: JsValue) {
|
||||
let Ok(state) = serde_wasm_bindgen::from_value(state) else {
|
||||
log::error!("Failed to deserialize persisted state");
|
||||
return;
|
||||
};
|
||||
|
||||
let message = PersistentStateMessage::LoadState { state };
|
||||
self.dispatch(message);
|
||||
pub fn load_persisted_state(&self, state: editor::messages::frontend::utility_types::PersistedState) {
|
||||
self.dispatch(PersistentStateMessage::LoadState { state });
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = loadDocumentContent)]
|
||||
@@ -494,9 +481,7 @@ impl EditorWrapper {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = splitPanelGroup)]
|
||||
pub fn split_panel_group(&self, target_group: u64, direction: String, tabs: JsValue, active_tab_index: usize) {
|
||||
let direction: DockingSplitDirection = serde_wasm_bindgen::from_value(JsValue::from_str(&direction)).unwrap();
|
||||
let tabs: Vec<PanelType> = serde_wasm_bindgen::from_value(tabs).unwrap();
|
||||
pub fn split_panel_group(&self, target_group: u64, direction: DockingSplitDirection, tabs: Vec<PanelType>, active_tab_index: usize) {
|
||||
let message = PortfolioMessage::SplitPanelGroup {
|
||||
target_group: PanelGroupId(target_group),
|
||||
direction,
|
||||
@@ -507,9 +492,8 @@ impl EditorWrapper {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = setPanelGroupSizes)]
|
||||
pub fn set_panel_group_sizes(&self, split_path: JsValue, sizes: JsValue) {
|
||||
let split_path: Vec<usize> = serde_wasm_bindgen::from_value(split_path).unwrap();
|
||||
let sizes: Vec<f64> = serde_wasm_bindgen::from_value(sizes).unwrap();
|
||||
pub fn set_panel_group_sizes(&self, split_path: Vec<u32>, sizes: Vec<f64>) {
|
||||
let split_path = split_path.into_iter().map(|i| i as usize).collect();
|
||||
let message = PortfolioMessage::SetPanelGroupSizes { split_path, sizes };
|
||||
self.dispatch(message);
|
||||
}
|
||||
@@ -645,13 +629,8 @@ impl EditorWrapper {
|
||||
|
||||
/// The font catalog has been loaded
|
||||
#[wasm_bindgen(js_name = onFontCatalogLoad)]
|
||||
pub fn on_font_catalog_load(&self, catalog: JsValue) -> Result<(), JsValue> {
|
||||
// Deserializing from TS type: `{ name: string; styles: { weight: number, italic: boolean, url: string }[] }[]`
|
||||
let families = serde_wasm_bindgen::from_value::<Vec<FontCatalogFamily>>(catalog)?;
|
||||
let message = PortfolioMessage::FontCatalogLoaded { catalog: FontCatalog(families) };
|
||||
self.dispatch(message);
|
||||
|
||||
Ok(())
|
||||
pub fn on_font_catalog_load(&self, catalog: Vec<FontCatalogFamily>) {
|
||||
self.dispatch(PortfolioMessage::FontCatalogLoaded { catalog: FontCatalog(catalog) });
|
||||
}
|
||||
|
||||
/// A font has been downloaded
|
||||
@@ -679,44 +658,29 @@ impl EditorWrapper {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update primary color with values on a scale from 0 to 1.
|
||||
/// Update primary color from sRGB bytes (the wire format at the JS boundary).
|
||||
#[wasm_bindgen(js_name = updatePrimaryColor)]
|
||||
pub fn update_primary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
||||
let Some(primary_color) = Color::from_rgbaf32(red, green, blue, alpha) else {
|
||||
return Err(Error::new("Invalid color").into());
|
||||
};
|
||||
|
||||
let message = ToolMessage::SelectWorkingColor {
|
||||
color: primary_color.to_linear_srgb(),
|
||||
pub fn update_primary_color(&self, color: SRGBA8) {
|
||||
self.dispatch(ToolMessage::SelectWorkingColor {
|
||||
color: Color::from(color),
|
||||
primary: true,
|
||||
};
|
||||
self.dispatch(message);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
/// Update secondary color with values on a scale from 0 to 1.
|
||||
/// Update secondary color from sRGB bytes (the wire format at the JS boundary).
|
||||
#[wasm_bindgen(js_name = updateSecondaryColor)]
|
||||
pub fn update_secondary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
||||
let Some(secondary_color) = Color::from_rgbaf32(red, green, blue, alpha) else {
|
||||
return Err(Error::new("Invalid color").into());
|
||||
};
|
||||
|
||||
let message = ToolMessage::SelectWorkingColor {
|
||||
color: secondary_color.to_linear_srgb(),
|
||||
pub fn update_secondary_color(&self, color: SRGBA8) {
|
||||
self.dispatch(ToolMessage::SelectWorkingColor {
|
||||
color: Color::from(color),
|
||||
primary: false,
|
||||
};
|
||||
self.dispatch(message);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
/// Initialize the Rust color picker handler with a starting value (used when the frontend `<ColorPicker>` opens).
|
||||
/// Initialize the Rust color picker handler with a starting value (used when the frontend `<ColorPicker />` opens).
|
||||
#[wasm_bindgen(js_name = openColorPicker)]
|
||||
pub fn open_color_picker(&self, initial_value: JsValue, allow_none: bool, disabled: bool) -> Result<(), JsValue> {
|
||||
let initial_value = serde_wasm_bindgen::from_value(initial_value).map_err(|e| Error::new(&format!("Invalid initial picker value: {e}")))?;
|
||||
pub fn open_color_picker(&self, initial_value: FillChoiceUI, allow_none: bool, disabled: bool) {
|
||||
let initial_value = FillChoice::from(&initial_value);
|
||||
self.dispatch(ColorPickerMessage::Open { initial_value, allow_none, disabled });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tell the Rust color picker handler that the popover is closing.
|
||||
@@ -725,14 +689,10 @@ impl EditorWrapper {
|
||||
self.dispatch(ColorPickerMessage::Close);
|
||||
}
|
||||
|
||||
/// Update the color of the currently-edited gradient stop
|
||||
/// Update the color of the currently-edited gradient stop, from sRGB bytes (the wire format at the JS boundary).
|
||||
#[wasm_bindgen(js_name = updateGradientStopColor)]
|
||||
pub fn update_gradient_stop_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
||||
let Some(color) = Color::from_rgbaf32(red, green, blue, alpha) else {
|
||||
return Err(Error::new("Invalid color").into());
|
||||
};
|
||||
self.dispatch(GradientToolMessage::UpdateStopColor { color: color.to_linear_srgb() });
|
||||
Ok(())
|
||||
pub fn update_gradient_stop_color(&self, color: SRGBA8) {
|
||||
self.dispatch(GradientToolMessage::UpdateStopColor { color: Color::from(color) });
|
||||
}
|
||||
|
||||
/// Start a new undo transaction for gradient stop color editing
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::{EDITOR_HAS_CRASHED, EDITOR_WRAPPER};
|
||||
use editor::application::Editor;
|
||||
use editor::messages::input_mapper::utility_types::input_keyboard::Key;
|
||||
use editor::messages::prelude::*;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::raster::color::Color;
|
||||
use js_sys::{Object, Reflect};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
@@ -119,7 +119,7 @@ pub(crate) fn auto_save_all_documents() {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn render_image_data_to_canvases(image_data: &[(u64, Image<Color>)]) {
|
||||
pub(crate) fn render_image_data_to_canvases(image_data: &[(u64, Image<SRGBA8>)]) {
|
||||
let window = match window() {
|
||||
Some(window) => window,
|
||||
None => {
|
||||
@@ -167,8 +167,9 @@ pub(crate) fn render_image_data_to_canvases(image_data: &[(u64, Image<Color>)])
|
||||
.expect("2d context was not found")
|
||||
.dyn_into::<CanvasRenderingContext2d>()
|
||||
.expect("Failed to cast context to CanvasRenderingContext2d");
|
||||
let u8_data: Vec<u8> = image.data.iter().flat_map(|color| color.to_rgba8_srgb()).collect();
|
||||
let clamped_u8_data = wasm_bindgen::Clamped(&u8_data[..]);
|
||||
// `SRGBA8` is `#[repr(C)]` of four `u8`s, so the data buffer is already the byte format the canvas expects
|
||||
let u8_data: &[u8] = bytemuck::cast_slice(&image.data);
|
||||
let clamped_u8_data = wasm_bindgen::Clamped(u8_data);
|
||||
match ImageData::new_with_u8_clamped_array_and_sh(clamped_u8_data, image.width, image.height) {
|
||||
Ok(image_data_obj) => {
|
||||
if context.put_image_data(&image_data_obj, 0., 0.).is_err() {
|
||||
|
||||
Reference in New Issue
Block a user