diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 91bcd099b6..716949cfaf 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -152,13 +152,18 @@ pub const SCALE_EFFECT: f64 = 0.5; // COLORS pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff"; pub const COLOR_OVERLAY_BLUE_50: &str = "#00a8ff80"; +pub const COLOR_OVERLAY_BLUE_25: &str = "#00a8ff40"; +pub const COLOR_OVERLAY_BLUE_05: &str = "#00a8ff0d"; pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848"; pub const COLOR_OVERLAY_YELLOW_DULL: &str = "#d7ba8b"; pub const COLOR_OVERLAY_GREEN: &str = "#63ce63"; +pub const COLOR_OVERLAY_GREEN_25: &str = "#63ce6340"; pub const COLOR_OVERLAY_RED: &str = "#ef5454"; +pub const COLOR_OVERLAY_RED_25: &str = "#ef545440"; pub const COLOR_OVERLAY_GRAY: &str = "#cccccc"; pub const COLOR_OVERLAY_GRAY_25: &str = "#cccccc40"; pub const COLOR_OVERLAY_WHITE: &str = "#ffffff"; +pub const COLOR_OVERLAY_WHITE_05: &str = "#ffffff0d"; pub const COLOR_OVERLAY_BLACK: &str = "#000000"; pub const COLOR_OVERLAY_BLACK_75: &str = "#000000bf"; diff --git a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs index c961f9052a..899fd767c9 100644 --- a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs +++ b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs @@ -9,7 +9,7 @@ use graphene_std::vector::style::FillChoice; fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) { let origin = document.snapping_state.grid.origin; - let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb(); + let grid_color = document.snapping_state.grid.color.as_str(); let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else { return; }; @@ -38,7 +38,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: } else { DVec2::new(secondary_pos, primary_end) }; - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None); } } } @@ -50,7 +50,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: // TODO: Implement this with a dashed line (`set_line_dash`), with integer spacing which is continuously adjusted to correct the accumulated error. fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) { let origin = document.snapping_state.grid.origin; - let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb(); + let grid_color = document.snapping_state.grid.color.as_str(); let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else { return; }; @@ -80,13 +80,13 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte let x_per_dot = (end.x - start.x) / total_dots; for dot_index in 0..=total_dots as usize { let exact_x = x_per_dot * dot_index as f64; - overlay_context.pixel(document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(), Some(&grid_color)) + overlay_context.pixel(document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(), Some(grid_color)) } } } fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) { - let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb(); + let grid_color = document.snapping_state.grid.color.as_str(); let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap(); let origin = document.snapping_state.grid.origin; let document_to_viewport = document @@ -111,7 +111,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m let x_pos = (((min_x - origin.x) / spacing).ceil() + line_index as f64) * spacing + origin.x; let start = DVec2::new(x_pos, min_y); let end = DVec2::new(x_pos, max_y); - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None); } for (tan, multiply) in [(tan_a, -1.), (tan_b, 1.)] { @@ -125,13 +125,13 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m let y_pos = (((inverse_project(&min_y) - origin.y) / spacing).ceil() + line_index as f64) * spacing + origin.y; let start = DVec2::new(min_x, project(&DVec2::new(min_x, y_pos))); let end = DVec2::new(max_x, project(&DVec2::new(max_x, y_pos))); - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None); } } } fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) { - let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb(); + let grid_color = document.snapping_state.grid.color.as_str(); let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap(); let origin = document.snapping_state.grid.origin; let document_to_viewport = document @@ -173,7 +173,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context overlay_context.dashed_line( document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), - Some(&grid_color), + Some(grid_color), None, Some(1.), Some((spacing_x / cos_a) * document_to_viewport.matrix2.x_axis.length() - 1.), @@ -220,13 +220,6 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec { } }) }; - let update_color = |grid, update: fn(&mut GridSnapping) -> Option<&mut Color>| { - update_val::(grid, move |grid, color| { - if let (Some(color), Some(update_color)) = (color.value.as_solid(), update(grid)) { - *update_color = color.to_linear_srgb(); - } - }) - }; let update_display = |grid, update: fn(&mut GridSnapping) -> Option<&mut bool>| { update_val::(grid, move |grid, checkbox| { if let Some(update) = update(grid) { @@ -285,10 +278,14 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec { Separator::new(SeparatorStyle::Related).widget_instance(), ]); color_widgets.push( - ColorInput::new(FillChoice::Solid(grid.grid_color.to_gamma_srgb())) + ColorInput::new(FillChoice::Solid(Color::from_hex_str(&grid.color).unwrap_or(Color::BLACK))) .tooltip_label("Grid Display Color") .allow_none(false) - .on_update(update_color(grid, |grid| Some(&mut grid.grid_color))) + .on_update(update_val::(grid, |grid, color| { + if let Some(color) = color.value.as_solid() { + grid.color = format!("#{}", color.to_rgba_hex_srgb_from_gamma()); + } + })) .widget_instance(), ); widgets.push(LayoutGroup::Row { widgets: color_widgets }); diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index 26f3a33a43..dec2fda58e 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -249,3 +249,15 @@ pub fn text_width(text: &str, font_size: f64) -> f64 { let bounds = text_context.bounding_box(text, &font, &GLOBAL_FONT_CACHE, typesetting, false); bounds.x } + +pub fn hex_to_rgba_u8(hex: &str) -> [u8; 4] { + let hex = hex.trim().trim_start_matches('#'); + if hex.len() != 6 && hex.len() != 8 { + return [0, 0, 0, 255]; + } + let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0); + let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0); + let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0); + let a = if hex.len() >= 8 { u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) } else { 255 }; + [r, g, b, a] +} diff --git a/editor/src/messages/portfolio/document/overlays/utility_types_native.rs b/editor/src/messages/portfolio/document/overlays/utility_types_native.rs index 139ed6ef69..5f854a7796 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types_native.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types_native.rs @@ -1,16 +1,15 @@ use crate::consts::{ - ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, + ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_WHITE_05, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, GRADIENT_MIDPOINT_DIAMOND_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE, }; -use crate::messages::portfolio::document::overlays::utility_functions::{GLOBAL_FONT_CACHE, GLOBAL_TEXT_CONTEXT}; +use crate::messages::portfolio::document::overlays::utility_functions::{GLOBAL_FONT_CACHE, GLOBAL_TEXT_CONTEXT, hex_to_rgba_u8}; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::prelude::Message; use crate::messages::prelude::ViewportMessageHandler; use core::borrow::Borrow; use core::f64::consts::{FRAC_PI_2, PI, TAU}; use glam::{DAffine2, DVec2}; -use graphene_std::Color; use graphene_std::math::quad::Quad; use graphene_std::subpath::{self, Subpath}; use graphene_std::table::Table; @@ -404,9 +403,9 @@ impl OverlayContext { self.internal().fill_path(subpaths, transform, color); } - /// Fills the area inside the path with a pattern. Assumes `color` is in gamma space. + /// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string. /// Used by the fill tool to show the area to be filled. - pub fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &Color) { + pub fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &str) { self.internal().fill_path_pattern(subpaths, transform, color); } @@ -457,11 +456,7 @@ impl OverlayContextInternal { } fn parse_color(color: &str) -> peniko::Color { - let hex = color.trim_start_matches('#'); - let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0); - let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0); - let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0); - let a = if hex.len() >= 8 { u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) } else { 255 }; + let [r, g, b, a] = hex_to_rgba_u8(color); peniko::Color::from_rgba8(r, g, b, a) } @@ -784,12 +779,7 @@ impl OverlayContextInternal { pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) { let sign = scale.signum(); - let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); + let fill_color = Some(COLOR_OVERLAY_WHITE_05); self.line(start + DVec2::X * radius * sign, start + DVec2::X * radius * scale.abs(), None, None); self.circle(start, radius, fill_color, None); self.circle(start, radius * scale.abs(), fill_color, None); @@ -820,15 +810,9 @@ impl OverlayContextInternal { // Hover ring if show_hover_ring { - let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.5) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let circle = kurbo::Circle::new((center.x, center.y), hover_ring_centerline_radius); self.scene - .stroke(&kurbo::Stroke::new(hover_ring_stroke_width), transform, Self::parse_color(&fill_color), None, &circle); + .stroke(&kurbo::Stroke::new(hover_ring_stroke_width), transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &circle); } // Arrows @@ -1067,16 +1051,16 @@ impl OverlayContextInternal { self.scene.fill(peniko::Fill::NonZero, self.get_transform(), Self::parse_color(color), None, &path); } - /// Fills the area inside the path with a pattern. Assumes `color` is in gamma space. + /// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string. /// Used by the fill tool to show the area to be filled. - fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &Color) { + fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &str) { const PATTERN_WIDTH: u32 = 4; const PATTERN_HEIGHT: u32 = 4; // Create a 4x4 pixel pattern with colored pixels at (0,0) and (2,2) // This matches the Canvas2D checkerboard pattern let mut data = vec![0u8; (PATTERN_WIDTH * PATTERN_HEIGHT * 4) as usize]; - let rgba = color.to_rgba8_srgb(); + let rgba = hex_to_rgba_u8(color); // ┌▄▄┬──┬──┬──┐ // ├▀▀┼──┼──┼──┤ diff --git a/editor/src/messages/portfolio/document/overlays/utility_types_web.rs b/editor/src/messages/portfolio/document/overlays/utility_types_web.rs index 77b0d7296a..3c249dcbf2 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types_web.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types_web.rs @@ -1,6 +1,6 @@ -use super::utility_functions::overlay_canvas_context; +use super::utility_functions::{hex_to_rgba_u8, overlay_canvas_context}; use crate::consts::{ - ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, + ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_WHITE_05, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, GRADIENT_MIDPOINT_DIAMOND_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SEGMENT_SELECTED_THICKNESS, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE, @@ -11,7 +11,6 @@ use crate::messages::viewport::ViewportMessageHandler; use core::borrow::Borrow; use core::f64::consts::{FRAC_PI_2, PI, TAU}; use glam::{DAffine2, DVec2}; -use graphene_std::Color; use graphene_std::math::quad::Quad; use graphene_std::subpath::Subpath; use graphene_std::vector::click_target::ClickTargetType; @@ -698,12 +697,7 @@ impl OverlayContext { pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) { let sign = scale.signum(); - let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); + let fill_color = Some(COLOR_OVERLAY_WHITE_05); self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None); self.circle(start, radius, fill_color, None); self.circle(start, radius * scale.abs(), fill_color, None); @@ -738,16 +732,10 @@ impl OverlayContext { // Hover ring if show_hover_ring { - let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.5) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - self.render_context.set_line_width(HOVER_RING_STROKE_WIDTH); self.render_context.begin_path(); self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect("Failed to draw hover ring"); - self.render_context.set_stroke_style_str(&fill_color); + self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50); self.render_context.stroke(); } @@ -1017,9 +1005,9 @@ impl OverlayContext { self.render_context.fill(); } - /// Fills the area inside the path with a pattern. Assumes `color` is in gamma space. + /// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string. /// Used by the fill tool to show the area to be filled. - pub fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &Color) { + pub fn fill_path_pattern(&mut self, subpaths: impl Iterator>>, transform: DAffine2, color: &str) { const PATTERN_WIDTH: usize = 4; const PATTERN_HEIGHT: usize = 4; @@ -1035,6 +1023,8 @@ impl OverlayContext { // 4x4 pixels, 4 components (RGBA) per pixel let mut data = [0_u8; 4 * PATTERN_WIDTH * PATTERN_HEIGHT]; + let rgba = hex_to_rgba_u8(color); + // ┌▄▄┬──┬──┬──┐ // ├▀▀┼──┼──┼──┤ // ├──┼──┼▄▄┼──┤ @@ -1043,7 +1033,7 @@ impl OverlayContext { let pixels = [(0, 0), (2, 2)]; for &(x, y) in &pixels { let index = (x + y * PATTERN_WIDTH) * 4; - data[index..index + 4].copy_from_slice(&color.to_rgba8_srgb()); + data[index..index + 4].copy_from_slice(&rgba); } let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(wasm_bindgen::Clamped(&data), PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap(); diff --git a/editor/src/messages/portfolio/document/utility_types/misc.rs b/editor/src/messages/portfolio/document/utility_types/misc.rs index 076030db5a..ef363ae108 100644 --- a/editor/src/messages/portfolio/document/utility_types/misc.rs +++ b/editor/src/messages/portfolio/document/utility_types/misc.rs @@ -1,6 +1,5 @@ use crate::consts::COLOR_OVERLAY_GRAY; use glam::DVec2; -use graphene_std::raster::Color; use std::fmt; #[repr(transparent)] @@ -216,7 +215,7 @@ pub struct GridSnapping { pub isometric_y_spacing: f64, pub isometric_angle_a: f64, pub isometric_angle_b: f64, - pub grid_color: Color, + pub color: String, pub dot_display: bool, } @@ -229,7 +228,7 @@ impl Default for GridSnapping { isometric_y_spacing: 1., isometric_angle_a: 30., isometric_angle_b: 30., - grid_color: Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(), + color: COLOR_OVERLAY_GRAY.to_string(), dot_display: false, } } diff --git a/editor/src/messages/tool/tool_messages/fill_tool.rs b/editor/src/messages/tool/tool_messages/fill_tool.rs index f29d73d1b7..9a069683be 100644 --- a/editor/src/messages/tool/tool_messages/fill_tool.rs +++ b/editor/src/messages/tool/tool_messages/fill_tool.rs @@ -110,7 +110,8 @@ impl Fsm for FillToolFsmState { // Get the layer the user is hovering over if let Some(layer) = document.click(input, viewport) { - overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &preview_color); + let color_hex = format!("#{}", preview_color.to_rgba_hex_srgb()); + overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &color_hex); } self diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index fc3bad458a..1a3b11a545 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -600,7 +600,7 @@ impl Fsm for GradientToolFsmState { let (start, end) = (transform.transform_point2(*start), transform.transform_point2(*end)); fn color_to_hex(color: graphene_std::Color) -> String { - format!("#{}", color.with_alpha(1.).to_rgba_hex_srgb()) + format!("#{}", color.to_rgb_hex_srgb_from_gamma()) } let start_hex = stops.color.first().map(|&c| color_to_hex(c)).unwrap_or(String::from(COLOR_OVERLAY_BLUE)); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index f8a50450c4..9b46386e6e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1,8 +1,9 @@ use super::select_tool::extend_lasso; use super::tool_prelude::*; use crate::consts::{ - COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GRAY, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DEFAULT_STROKE_WIDTH, DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, - DRILL_THROUGH_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE, + COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_05, COLOR_OVERLAY_GRAY, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_GREEN_25, COLOR_OVERLAY_RED, COLOR_OVERLAY_RED_25, DEFAULT_STROKE_WIDTH, + DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, DRILL_THROUGH_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, + SELECTION_THRESHOLD, SELECTION_TOLERANCE, }; use crate::messages::clipboard::utility_types::ClipboardContent; use crate::messages::input_mapper::utility_types::macros::action_shortcut_manual; @@ -1888,12 +1889,7 @@ impl Fsm for PathToolFsmState { } } Self::Drawing { selection_shape } => { - let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); + let fill_color = Some(COLOR_OVERLAY_BLUE_05); let selection_mode = match tool_action_data.preferences.get_selection_mode() { SelectionMode::Directional => tool_data.calculate_selection_mode_from_direction(document.metadata()), @@ -1977,22 +1973,14 @@ impl Fsm for PathToolFsmState { let origin = tool_data.drag_start_pos; let viewport_diagonal = viewport.size().into_dvec2().length(); - let faded = |color: &str| { - let mut color = graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.25) - .to_rgba_hex_srgb(); - color.insert(0, '#'); - color - }; match axis { Axis::Y => { overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(COLOR_OVERLAY_GREEN), None); - overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(&faded(COLOR_OVERLAY_RED)), None); + overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(COLOR_OVERLAY_RED_25), None); } Axis::X | Axis::Both => { overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(COLOR_OVERLAY_RED), None); - overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(&faded(COLOR_OVERLAY_GREEN)), None); + overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(COLOR_OVERLAY_GREEN_25), None); } } } diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index d5d905cc71..ceab12bad3 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1,5 +1,5 @@ use super::tool_prelude::*; -use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE}; +use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_05, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE}; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; @@ -1783,12 +1783,8 @@ impl Fsm for PenToolFsmState { }) .collect(); - let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - overlay_context.fill_path(subpaths.iter(), transform, fill_color.as_str()); + let fill_color = COLOR_OVERLAY_BLUE_05; + overlay_context.fill_path(subpaths.iter(), transform, fill_color); } } } diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 1fb99f443a..61012e6a0c 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -689,13 +689,7 @@ impl Fsm for SelectToolFsmState { .parent(document.metadata()) .is_some_and(|parent| selected.selected_layers_contains(parent, document.metadata())) }) { - let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.5) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); - hover_overlay_draw(new_selected, fill_color); + hover_overlay_draw(new_selected, Some(COLOR_OVERLAY_BLUE_50)); } } } @@ -892,23 +886,15 @@ impl Fsm for SelectToolFsmState { .map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds)) .map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X)); - let (direction, color) = match axis { - Axis::X => (e0, COLOR_OVERLAY_RED), - Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN), + let (direction, color, color_faded) = match axis { + Axis::X => (e0, COLOR_OVERLAY_RED, COLOR_OVERLAY_RED_25), + Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN, COLOR_OVERLAY_GREEN_25), _ => unreachable!(), }; let viewport_diagonal = viewport.size().into_dvec2().length(); - let color = if !hover { - color - } else { - let color_string = &graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.25) - .to_rgba_hex_srgb(); - &format!("#{color_string}") - }; + let color = if !hover { color } else { color_faded }; let line_center = tool_data.line_center; overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None); } @@ -926,16 +912,10 @@ impl Fsm for SelectToolFsmState { let perp = edge.perp(); let (edge_color, perp_color) = if edge.x.abs() > edge.y.abs() { - (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN) + (COLOR_OVERLAY_RED, COLOR_OVERLAY_GREEN_25) } else { - (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED) + (COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED_25) }; - let mut perp_color = graphene_std::Color::from_rgb_hex_for_overlays(perp_color.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.25) - .to_rgba_hex_srgb(); - perp_color.insert(0, '#'); - let perp_color = perp_color.as_str(); overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None); overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(perp_color), None); } @@ -978,12 +958,7 @@ impl Fsm for SelectToolFsmState { } // Update the selection box - let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); - fill_color.insert(0, '#'); - let fill_color = Some(fill_color.as_str()); + let fill_color = Some(COLOR_OVERLAY_BLUE_05); let polygon = &tool_data.lasso_polygon; diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index dd1d87b90e..69593190e4 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -1,7 +1,7 @@ #![allow(clippy::too_many_arguments)] use super::tool_prelude::*; -use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_RED, DRAG_THRESHOLD}; +use crate::consts::{COLOR_OVERLAY_BLUE_05, COLOR_OVERLAY_RED, DRAG_THRESHOLD}; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; @@ -570,10 +570,7 @@ impl Fsm for TextToolFsmState { .. } = transition_data; let font_cache = &persistent_data.font_cache; - let fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()) - .unwrap() - .with_alpha(0.05) - .to_rgba_hex_srgb(); + let fill_color = COLOR_OVERLAY_BLUE_05; let ToolMessage::Text(event) = event else { return self }; match (self, event) { @@ -585,7 +582,7 @@ impl Fsm for TextToolFsmState { if far.x != 0. && far.y != 0. { let quad = Quad::from_box([DVec2::ZERO, far]); let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad; - overlay_context.quad(transformed_quad, None, Some(&("#".to_string() + &fill_color))); + overlay_context.quad(transformed_quad, None, Some(fill_color)); } } @@ -598,14 +595,10 @@ impl Fsm for TextToolFsmState { // Draw a bounding box on the layers to be selected for layer in document.intersect_quad_no_artboards(quad, viewport) { - overlay_context.quad( - Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])), - None, - Some(&("#".to_string() + &fill_color)), - ); + overlay_context.quad(Quad::from_box(document.metadata().bounding_box_viewport(layer).unwrap_or([DVec2::ZERO; 2])), None, Some(fill_color)); } - overlay_context.quad(quad, None, Some(&("#".to_string() + &fill_color))); + overlay_context.quad(quad, None, Some(fill_color)); } // TODO: implement bounding box for multiple layers diff --git a/node-graph/libraries/no-std-types/src/color/color_types.rs b/node-graph/libraries/no-std-types/src/color/color_types.rs index 4b4d7f00be..1c5ce2ef3d 100644 --- a/node-graph/libraries/no-std-types/src/color/color_types.rs +++ b/node-graph/libraries/no-std-types/src/color/color_types.rs @@ -426,12 +426,10 @@ impl Color { /// ``` #[inline(always)] pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color { - let map_range = |int_color| int_color as f32 / 255.; - - let red = map_range(red); - let green = map_range(green); - let blue = map_range(blue); - let alpha = map_range(alpha); + let red = red as f32 / 255.; + let green = green as f32 / 255.; + let blue = blue as f32 / 255.; + let alpha = alpha as f32 / 255.; Color { red, green, blue, alpha }.to_linear_srgb().map_rgb(|channel| channel * alpha) } @@ -941,70 +939,19 @@ impl Color { [hue, saturation, lightness, self.alpha] } - // TODO: This incorrectly handles gamma/linear and premultiplied alpha. For now, this can only be used for overlay drawing, not artwork. - // TODO: Remove this function and have overlays directly use the hex colors and not use the `Color` struct at all. - /// Creates a color from a 6-character RGB hex string (without a # prefix). - /// - /// ``` - /// use core_types::color::Color; - /// let color = Color::from_rgb_hex_for_overlays("7C67FA").unwrap(); - /// ``` - pub fn from_rgb_hex_for_overlays(color_str: &str) -> Option { - if color_str.len() != 6 { - return None; - } - let r = u8::from_str_radix(&color_str[0..2], 16).ok()?; - let g = u8::from_str_radix(&color_str[2..4], 16).ok()?; - let b = u8::from_str_radix(&color_str[4..6], 16).ok()?; - - Some(Color::from_rgb8_srgb(r, g, b)) - } - - /// Creates a color from an 8-character RGBA hex string (without a # prefix). - /// - /// ``` - /// use core_types::color::Color; - /// let color = Color::from_rgba_str("7C67FA61").unwrap(); - /// ``` - pub fn from_rgba_str(color_str: &str) -> Option { - if color_str.len() != 8 { - return None; - } - let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.; - let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.; - let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.; - let alpha = u8::from_str_radix(&color_str[6..8], 16).ok()? as f32 / 255.; - - Some(Color { red, green, blue, alpha }) - } - - /// Creates a color from a 6-character RGB hex string (without a # prefix). - /// - /// ``` - /// use core_types::color::Color; - /// let color = Color::from_rgb_str("7C67FA").unwrap(); - /// ``` - pub fn from_rgb_str(color_str: &str) -> Option { - if color_str.len() != 6 { - return None; - } - let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.; - let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.; - let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.; - - Some(Color { red, green, blue, alpha: 1. }) - } - /// Creates a color from a hex color code string with an optional `#` prefix, such as `#RRGGBB`, `RRGGBB`, `#RRGGBBAA`, or `RRGGBBAA`. /// Returns `None` for invalid or unrecognized strings. #[cfg(feature = "std")] pub fn from_hex_str(hex: &str) -> Option { let hex = hex.trim().trim_start_matches('#'); - match hex.len() { - 6 => Color::from_rgb_str(hex), - 8 => Color::from_rgba_str(hex), - _ => None, + if hex.len() != 6 && hex.len() != 8 { + return None; } + let red = u8::from_str_radix(&hex[0..2], 16).ok()? as f32 / 255.; + let green = u8::from_str_radix(&hex[2..4], 16).ok()? as f32 / 255.; + let blue = u8::from_str_radix(&hex[4..6], 16).ok()? as f32 / 255.; + let alpha = if hex.len() == 8 { u8::from_str_radix(&hex[6..8], 16).ok()? as f32 / 255. } else { 1. }; + Some(Color { red, green, blue, alpha }) } /// Linearly interpolates between two colors based on t.