From 0887cddee774808356eecc76ab3902fbc4b8c530 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 16:20:29 +0100 Subject: [PATCH 01/15] Change Color type to be f32 internally but no other changes --- internal/core/graphics/color.rs | 161 ++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 68 deletions(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 3bf87cd699c..502127fea85 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -47,75 +47,103 @@ pub struct RgbaColor { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(C)] pub struct Color { - red: u8, - green: u8, - blue: u8, - alpha: u8, + red: f32, + green: f32, + blue: f32, + alpha: f32, } -impl From> for Color { - fn from(col: RgbaColor) -> Self { - Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } - } +const fn quantize(value: f32) -> u8 { + (value * 255.0).round() as u8 } -impl From for RgbaColor { - fn from(col: Color) -> Self { - RgbaColor { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } +const fn unquantize(value: u8) -> f32 { + (value as f32) / 255.0 +} + +#[test] +fn unquantize_roundtrip() { + for v in 0..=255 { + assert_eq!(v, quantize(unquantize(v))); } } impl From> for RgbaColor { fn from(col: RgbaColor) -> Self { Self { - red: (col.red as f32) / 255.0, - green: (col.green as f32) / 255.0, - blue: (col.blue as f32) / 255.0, - alpha: (col.alpha as f32) / 255.0, + red: unquantize(col.red), + green: unquantize(col.green), + blue: unquantize(col.blue), + alpha: unquantize(col.alpha), + } + } +} + +impl From> for RgbaColor { + fn from(col: RgbaColor) -> Self { + Self { + red: quantize(col.red), + green: quantize(col.green), + blue: quantize(col.blue), + alpha: quantize(col.alpha), } } } impl From for RgbaColor { fn from(col: Color) -> Self { - let u8col: RgbaColor = col.into(); - u8col.into() + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } } } impl From> for Color { fn from(col: RgbaColor) -> Self { - Self { - red: (col.red * 255.).round() as u8, - green: (col.green * 255.).round() as u8, - blue: (col.blue * 255.).round() as u8, - alpha: (col.alpha * 255.).round() as u8, - } + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + } +} + +impl From> for Color { + fn from(col: RgbaColor) -> Self { + let col: RgbaColor = col.into(); + col.into() + } +} + +impl From for RgbaColor { + fn from(col: Color) -> Self { + let f32col: RgbaColor = col.into(); + f32col.into() } } impl Color { /// Construct a color from an integer encoded as `0xAARRGGBB` pub const fn from_argb_encoded(encoded: u32) -> Color { - Self { - red: (encoded >> 16) as u8, - green: (encoded >> 8) as u8, - blue: encoded as u8, - alpha: (encoded >> 24) as u8, - } + Self::from_argb_u8( + (encoded >> 24) as u8, + (encoded >> 16) as u8, + (encoded >> 8) as u8, + encoded as u8, + ) } /// Returns `(alpha, red, green, blue)` encoded as u32 pub fn as_argb_encoded(&self) -> u32 { - ((self.red as u32) << 16) - | ((self.green as u32) << 8) - | (self.blue as u32) - | ((self.alpha as u32) << 24) + let col: RgbaColor = (*self).into(); + ((col.red as u32) << 16) + | ((col.green as u32) << 8) + | (col.blue as u32) + | ((col.alpha as u32) << 24) } /// Construct a color from the alpha, red, green and blue color channel parameters. pub const fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self { - Self { red, green, blue, alpha } + Self { + red: unquantize(red), + green: unquantize(green), + blue: unquantize(blue), + alpha: unquantize(alpha), + } } /// Construct a color from the red, green and blue color channel parameters. The alpha @@ -126,7 +154,7 @@ impl Color { /// Construct a color from the alpha, red, green and blue color channel parameters. pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self { - RgbaColor { alpha, red, green, blue }.into() + Self { alpha, red, green, blue } } /// Construct a color from the red, green and blue color channel parameters. The alpha @@ -162,25 +190,25 @@ impl Color { /// Returns the red channel of the color as u8 in the range 0..255. #[inline(always)] pub fn red(self) -> u8 { - self.red + quantize(self.red) } /// Returns the green channel of the color as u8 in the range 0..255. #[inline(always)] pub fn green(self) -> u8 { - self.green + quantize(self.green) } /// Returns the blue channel of the color as u8 in the range 0..255. #[inline(always)] pub fn blue(self) -> u8 { - self.blue + quantize(self.blue) } /// Returns the alpha channel of the color as u8 in the range 0..255. #[inline(always)] pub fn alpha(self) -> u8 { - self.alpha + quantize(self.alpha) } /// Returns a new version of this color that has the brightness increased @@ -220,30 +248,28 @@ impl Color { /// Decreasing the opacity of a red color by half: /// ``` /// # use i_slint_core::graphics::Color; - /// let red = Color::from_argb_u8(255, 255, 0, 0); - /// assert_eq!(red.transparentize(0.5), Color::from_argb_u8(128, 255, 0, 0)); + /// let red = Color::from_argb_f32(1.0, 1.0, 0.0, 0.0); + /// assert_eq!(red.transparentize(0.5), Color::from_argb_f32(0.5, 1.0, 0.0, 0.0)); /// ``` /// /// Decreasing the opacity of a blue color by 20%: /// ``` /// # use i_slint_core::graphics::Color; - /// let blue = Color::from_argb_u8(200, 0, 0, 255); - /// assert_eq!(blue.transparentize(0.2), Color::from_argb_u8(160, 0, 0, 255)); + /// let blue = Color::from_argb_f32(1.0, 0.0, 0.0, 1.0); + /// assert_eq!(blue.transparentize(0.2), Color::from_argb_f32(0.8, 0.0, 0.0, 1.0)); /// ``` /// /// Negative values increase the opacity /// /// ``` /// # use i_slint_core::graphics::Color; - /// let blue = Color::from_argb_u8(200, 0, 0, 255); - /// assert_eq!(blue.transparentize(-0.1), Color::from_argb_u8(220, 0, 0, 255)); + /// let blue = Color::from_argb_f32(0.5, 0.0, 0.0, 1.0); + /// assert_eq!(blue.transparentize(-0.1), Color::from_argb_f32(0.55, 0.0, 0.0, 1.0)); /// ``` #[must_use] pub fn transparentize(&self, factor: f32) -> Self { let mut color = *self; - color.alpha = ((self.alpha as f32) * (1.0 - factor)) - .round() - .clamp(u8::MIN as f32, u8::MAX as f32) as u8; + color.alpha = (self.alpha as f32) * (1.0 - factor); color } @@ -255,17 +281,17 @@ impl Color { /// Mix red with black half-and-half: /// ``` /// # use i_slint_core::graphics::Color; - /// let red = Color::from_rgb_u8(255, 0, 0); - /// let black = Color::from_rgb_u8(0, 0, 0); - /// assert_eq!(red.mix(&black, 0.5), Color::from_rgb_u8(128, 0, 0)); + /// let red = Color::from_rgb_f32(1.0, 0.0, 0.0); + /// let black = Color::from_rgb_f32(0.0, 0.0, 0.0); + /// assert_eq!(red.mix(&black, 0.5), Color::from_rgb_f32(0.5, 0.0, 0.0)); /// ``` /// - /// Mix Purple with OrangeRed, with `75%` purpe and `25%` orange red ratio: + /// Mix Purple with OrangeRed, with `80%` purple and `20%` orange red ratio: /// ``` /// # use i_slint_core::graphics::Color; - /// let purple = Color::from_rgb_u8(128, 0, 128); - /// let orange_red = Color::from_rgb_u8(255, 69, 0); - /// assert_eq!(purple.mix(&orange_red, 0.75), Color::from_rgb_u8(160, 17, 96)); + /// let purple = Color::from_rgb_f32(0.5, 0.5, 0.5); + /// let orange_red = Color::from_rgb_f32(1.0, 0.27, 0.0); + /// assert_eq!(purple.mix(&orange_red, 0.8), Color::from_rgb_f32(0.6, 0.454, 0.4)); /// ``` #[must_use] pub fn mix(&self, other: &Self, factor: f32) -> Self { @@ -275,9 +301,8 @@ impl Color { // * NOTE: Considering the spec (textual): // * - fn lerp(v1: u8, v2: u8, f: f32) -> u8 { - (v1 as f32 * f + v2 as f32 * (1.0 - f)).clamp(u8::MIN as f32, u8::MAX as f32).round() - as u8 + fn lerp(v1: f32, v2: f32, f: f32) -> f32 { + (v1 * f + v2 * (1.0 - f)).clamp(0.0, 1.0) } let original_factor = factor.clamp(0.0, 1.0); @@ -479,20 +504,20 @@ fn test_rgb_to_hsv() { #[test] fn test_brighter_darker() { - let blue = Color::from_rgb_u8(0, 0, 128); - assert_eq!(blue.brighter(0.5), Color::from_rgb_u8(0, 0, 192)); - assert_eq!(blue.darker(0.5), Color::from_rgb_u8(0, 0, 85)); + let blue = Color::from_rgb_f32(0.0, 0.0, 0.5); + assert_eq!(blue.brighter(0.5), Color::from_rgb_f32(0.0, 0.0, 0.75)); + assert_eq!(blue.darker(0.5), Color::from_rgb_f32(0.0, 0.0, 1.0 / 3.0)); } #[test] fn test_transparent_transition() { - let color = Color::from_argb_u8(0, 0, 0, 0); - let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.25); - assert_eq!(interpolated, Color::from_argb_u8(64, 211, 211, 211)); - let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.5); - assert_eq!(interpolated, Color::from_argb_u8(128, 211, 211, 211)); - let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.75); - assert_eq!(interpolated, Color::from_argb_u8(191, 211, 211, 211)); + let color = Color::from_argb_f32(0.0, 0.0, 0.0, 0.0); + let interpolated = color.interpolate(&Color::from_rgb_f32(0.8, 0.8, 0.8), 0.25); + assert_eq!(interpolated, Color::from_argb_f32(0.25, 0.8, 0.8, 0.8)); + let interpolated = color.interpolate(&Color::from_rgb_f32(0.8, 0.8, 0.8), 0.5); + assert_eq!(interpolated, Color::from_argb_f32(0.5, 0.8, 0.8, 0.8)); + let interpolated = color.interpolate(&Color::from_rgb_f32(0.8, 0.8, 0.8), 0.75); + assert_eq!(interpolated, Color::from_argb_f32(0.75, 0.8, 0.8, 0.8)); } #[cfg(feature = "ffi")] From 6d8e1f799fc23141c40fe369dd511b594727d337 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 16:46:41 +0100 Subject: [PATCH 02/15] Add missing clamp --- internal/core/graphics/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 502127fea85..18ba1288bfa 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -269,7 +269,7 @@ impl Color { #[must_use] pub fn transparentize(&self, factor: f32) -> Self { let mut color = *self; - color.alpha = (self.alpha as f32) * (1.0 - factor); + color.alpha = ((self.alpha as f32) * (1.0 - factor)).clamp(0.0, 1.0); color } From 41b8edd4acdc9a6ab47ea483ddbe47fb792c0981 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 16:57:27 +0100 Subject: [PATCH 03/15] Add round function as we're not on MSRV 1.90+ --- internal/core/graphics/color.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 18ba1288bfa..9568d06e5a8 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -53,8 +53,17 @@ pub struct Color { alpha: f32, } +// until slint uses rust 1.90 as MSRV. +const fn round(mut value: f32) -> u8 { + if value % 1.0 > 0.5 { + value += 0.5; + } + + value as _ +} + const fn quantize(value: f32) -> u8 { - (value * 255.0).round() as u8 + round(value * 255.0) } const fn unquantize(value: u8) -> f32 { From ca566a639e8bdb45aa744f924260795c598c3ee6 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:11:01 +0100 Subject: [PATCH 04/15] Prepare for being able to switch between u8s and f32s --- internal/core/graphics/color.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 9568d06e5a8..f5b2b5bfacc 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -163,7 +163,7 @@ impl Color { /// Construct a color from the alpha, red, green and blue color channel parameters. pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self { - Self { alpha, red, green, blue } + RgbaColor {alpha,red,green,blue}.into() } /// Construct a color from the red, green and blue color channel parameters. The alpha @@ -316,11 +316,11 @@ impl Color { let original_factor = factor.clamp(0.0, 1.0); - let self_opacity = RgbaColor::::from(*self).alpha; - let other_opacity = RgbaColor::::from(*other).alpha; + let col = RgbaColor::::from(*self); + let other = RgbaColor::::from(*other); let normal_weight = 2.0 * original_factor - 1.0; - let alpha_distance = self_opacity - other_opacity; + let alpha_distance = col.alpha - other.alpha; let weight_by_distance = normal_weight * alpha_distance; // As to not divide by 0.0 @@ -332,13 +332,13 @@ impl Color { let channels_factor = (combined_weight + 1.0) / 2.0; - let red = lerp(self.red, other.red, channels_factor); - let green = lerp(self.green, other.green, channels_factor); - let blue = lerp(self.blue, other.blue, channels_factor); + let red = lerp(col.red, other.red, channels_factor); + let green = lerp(col.green, other.green, channels_factor); + let blue = lerp(col.blue, other.blue, channels_factor); - let alpha = lerp(self.alpha, other.alpha, original_factor); + let alpha = lerp(col.alpha, other.alpha, original_factor); - Self { red, green, blue, alpha } + RgbaColor { red, green, blue, alpha }.into() } /// Returns a new version of this color with the opacity set to `alpha`. From f9e1511c4d15ba3e81f4852b12a58cf0229302de Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:19:17 +0000 Subject: [PATCH 05/15] [autofix.ci] apply automated fixes --- internal/core/graphics/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index f5b2b5bfacc..29f2b1a04e4 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -163,7 +163,7 @@ impl Color { /// Construct a color from the alpha, red, green and blue color channel parameters. pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self { - RgbaColor {alpha,red,green,blue}.into() + RgbaColor { alpha, red, green, blue }.into() } /// Construct a color from the red, green and blue color channel parameters. The alpha From b1079017b9c37a18fbfaa89c99e6003f4c04b61f Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 06/15] Fix Display impl --- internal/core/graphics/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 29f2b1a04e4..4bbe73e9e20 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -358,7 +358,7 @@ impl InterpolatedPropertyValue for Color { impl core::fmt::Display for Color { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "argb({}, {}, {}, {})", self.alpha, self.red, self.green, self.blue) + write!(f, "argb({}, {}, {}, {})", self.alpha(), self.red(), self.green(), self.blue()) } } From 92217e06f8ac3f7c38d35333750152e6600f4e1c Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 07/15] Add feature flag for 8-bit color values. Name to be bikeshed --- internal/core/Cargo.toml | 1 + internal/core/graphics/color.rs | 102 +++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 27 deletions(-) diff --git a/internal/core/Cargo.toml b/internal/core/Cargo.toml index 10a461c245a..fcc6ac91dba 100644 --- a/internal/core/Cargo.toml +++ b/internal/core/Cargo.toml @@ -74,6 +74,7 @@ unstable-wgpu-27 = ["dep:wgpu-27"] tr = ["dep:tr"] +8-bit-color = [] default = ["std", "unicode"] shared-parley = ["shared-fontique", "dep:parley", "dep:skrifa"] diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 4bbe73e9e20..55e499368e8 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -27,6 +27,11 @@ pub struct RgbaColor { pub blue: T, } +#[cfg(not(feature = "8-bit-color"))] +type Channel = f32; +#[cfg(feature = "8-bit-color")] +type Channel = u8; + /// Color represents a color in the Slint run-time, represented using 8-bit channels for /// red, green, blue and the alpha (opacity). /// It can be conveniently converted using the `to_` and `from_` (a)rgb helper functions: @@ -47,10 +52,10 @@ pub struct RgbaColor { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(C)] pub struct Color { - red: f32, - green: f32, - blue: f32, - alpha: f32, + red: Channel, + green: Channel, + blue: Channel, + alpha: Channel, } // until slint uses rust 1.90 as MSRV. @@ -78,6 +83,7 @@ fn unquantize_roundtrip() { } impl From> for RgbaColor { + #[inline] fn from(col: RgbaColor) -> Self { Self { red: unquantize(col.red), @@ -89,6 +95,7 @@ impl From> for RgbaColor { } impl From> for RgbaColor { + #[inline] fn from(col: RgbaColor) -> Self { Self { red: quantize(col.red), @@ -100,28 +107,62 @@ impl From> for RgbaColor { } impl From for RgbaColor { + #[inline] fn from(col: Color) -> Self { - Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + #[cfg(not(feature = "8-bit-color"))] + { + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + } + #[cfg(feature = "8-bit-color")] + { + let col: RgbaColor = col.into(); + col.into() + } } } impl From> for Color { + #[inline] fn from(col: RgbaColor) -> Self { - Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + #[cfg(not(feature = "8-bit-color"))] + { + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + } + #[cfg(feature = "8-bit-color")] + { + let col: RgbaColor = col.into(); + col.into() + } } } impl From> for Color { + #[inline] fn from(col: RgbaColor) -> Self { - let col: RgbaColor = col.into(); - col.into() + #[cfg(not(feature = "8-bit-color"))] + { + let col: RgbaColor = col.into(); + col.into() + } + #[cfg(feature = "8-bit-color")] + { + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + } } } impl From for RgbaColor { + #[inline] fn from(col: Color) -> Self { - let f32col: RgbaColor = col.into(); - f32col.into() + #[cfg(not(feature = "8-bit-color"))] + { + let col: RgbaColor = col.into(); + col.into() + } + #[cfg(feature = "8-bit-color")] + { + Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } + } } } @@ -147,11 +188,18 @@ impl Color { /// Construct a color from the alpha, red, green and blue color channel parameters. pub const fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self { - Self { - red: unquantize(red), - green: unquantize(green), - blue: unquantize(blue), - alpha: unquantize(alpha), + #[cfg(not(feature = "8-bit-color"))] + { + Self { + red: unquantize(red), + green: unquantize(green), + blue: unquantize(blue), + alpha: unquantize(alpha), + } + } + #[cfg(feature = "8-bit-color")] + { + Self { red, green, blue, alpha } } } @@ -199,25 +247,25 @@ impl Color { /// Returns the red channel of the color as u8 in the range 0..255. #[inline(always)] pub fn red(self) -> u8 { - quantize(self.red) + RgbaColor::::from(self).red } /// Returns the green channel of the color as u8 in the range 0..255. #[inline(always)] pub fn green(self) -> u8 { - quantize(self.green) + RgbaColor::::from(self).green } /// Returns the blue channel of the color as u8 in the range 0..255. #[inline(always)] pub fn blue(self) -> u8 { - quantize(self.blue) + RgbaColor::::from(self).blue } /// Returns the alpha channel of the color as u8 in the range 0..255. #[inline(always)] pub fn alpha(self) -> u8 { - quantize(self.alpha) + RgbaColor::::from(self).alpha } /// Returns a new version of this color that has the brightness increased @@ -277,9 +325,9 @@ impl Color { /// ``` #[must_use] pub fn transparentize(&self, factor: f32) -> Self { - let mut color = *self; - color.alpha = ((self.alpha as f32) * (1.0 - factor)).clamp(0.0, 1.0); - color + let mut col: RgbaColor = (*self).into(); + col.alpha = (col.alpha * (1.0 - factor)).clamp(0.0, 1.0); + col.into() } /// Returns a new color that is a mix of this color and `other`. The specified factor is @@ -295,12 +343,12 @@ impl Color { /// assert_eq!(red.mix(&black, 0.5), Color::from_rgb_f32(0.5, 0.0, 0.0)); /// ``` /// - /// Mix Purple with OrangeRed, with `80%` purple and `20%` orange red ratio: + /// Mix Purple with OrangeRed, with `75%` purpe and `25%` orange red ratio: /// ``` - /// # use i_slint_core::graphics::Color; - /// let purple = Color::from_rgb_f32(0.5, 0.5, 0.5); - /// let orange_red = Color::from_rgb_f32(1.0, 0.27, 0.0); - /// assert_eq!(purple.mix(&orange_red, 0.8), Color::from_rgb_f32(0.6, 0.454, 0.4)); + /// # use i_slint_core::graphics::{Color, RgbaColor}; + /// let purple = Color::from_rgb_u8(128, 0, 128); + /// let orange_red = Color::from_rgb_u8(255, 69, 0); + /// assert_eq!(purple.mix(&orange_red, 0.75), Color::from_rgb_f32(0.6264706, 0.06764706, 0.37647063)); /// ``` #[must_use] pub fn mix(&self, other: &Self, factor: f32) -> Self { From 23673c4b9c11c02a57dadda100c02191d61b3024 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 08/15] Fix brighter-darker test --- internal/core/graphics/color.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 55e499368e8..5ad9581592d 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -561,9 +561,9 @@ fn test_rgb_to_hsv() { #[test] fn test_brighter_darker() { - let blue = Color::from_rgb_f32(0.0, 0.0, 0.5); - assert_eq!(blue.brighter(0.5), Color::from_rgb_f32(0.0, 0.0, 0.75)); - assert_eq!(blue.darker(0.5), Color::from_rgb_f32(0.0, 0.0, 1.0 / 3.0)); + let blue = Color::from_rgb_u8(0, 0, 128); + assert_eq!(blue.brighter(0.5), Color::from_rgb_f32(0.0, 0.0, 0.75294125)); + assert_eq!(blue.darker(0.5), Color::from_rgb_f32(0.0, 0.0, 0.33464053)); } #[test] From bb9aa3d5681c1a85f296269956d3df53499c40f9 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 09/15] Update test screenshot --- .../software/basic/text-input-selection.png | Bin 3941 -> 3944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/screenshots/references/software/basic/text-input-selection.png b/tests/screenshots/references/software/basic/text-input-selection.png index 1ba1539a2514c1165b9f06d8a2ebd242ead70bf4..bf8465a82bcc9c528aa3f49e50b08397ef614090 100644 GIT binary patch delta 2774 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 T%U5&QZ}Y2LUTJ)J_o06Q5s+|Z From 2d7f940e6e85235e70600e4ad1d02ad76656d381 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 10/15] Remove unused Float trait import --- internal/core/graphics/color.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 5ad9581592d..9f876214902 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -7,9 +7,6 @@ This module contains color related types for the run-time library. use crate::properties::InterpolatedPropertyValue; -#[cfg(not(feature = "std"))] -use num_traits::float::Float; - /// RgbaColor stores the red, green, blue and alpha components of a color /// with the precision of the generic parameter T. For example if T is f32, /// the values are normalized between 0 and 1. If T is u8, they values range From aad761e6d29db3699fc6ffa02115964b9817f8c8 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Mon, 10 Nov 2025 17:14:04 +0100 Subject: [PATCH 11/15] Add cfg thing for not(cbindgen) --- internal/core/graphics/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 9f876214902..9a420be65c6 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -26,7 +26,7 @@ pub struct RgbaColor { #[cfg(not(feature = "8-bit-color"))] type Channel = f32; -#[cfg(feature = "8-bit-color")] +#[cfg(all(feature = "8-bit-color", not(cbindgen)))] type Channel = u8; /// Color represents a color in the Slint run-time, represented using 8-bit channels for From d43ab94f2d6bfc38a99e809d82e3fc325915b246 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Tue, 11 Nov 2025 13:15:12 +0100 Subject: [PATCH 12/15] Change Channel to float in cbindgen --- api/cpp/cbindgen.rs | 2 ++ internal/core/graphics/color.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index 94a77e8cf64..4d22ffcdcd7 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -234,6 +234,7 @@ fn default_config() -> cbindgen::Config { // therefore it is ok to reinterpret_cast ("MenuEntryModel".into(), "std::shared_ptr>".into()), ("Coord".into(), "float".into()), + ("Channel".into(), "float".into()), ] .iter() .cloned() @@ -397,6 +398,7 @@ fn gen_corelib( "MenuEntryModel", "MenuEntryArg", "Coord", + "Channel", "LogicalRect", "LogicalPoint", "LogicalPosition", diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 9a420be65c6..9f876214902 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -26,7 +26,7 @@ pub struct RgbaColor { #[cfg(not(feature = "8-bit-color"))] type Channel = f32; -#[cfg(all(feature = "8-bit-color", not(cbindgen)))] +#[cfg(feature = "8-bit-color")] type Channel = u8; /// Color represents a color in the Slint run-time, represented using 8-bit channels for From 4646821504eda1699db76b2390407db2cf2abf9b Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Tue, 11 Nov 2025 13:53:12 +0100 Subject: [PATCH 13/15] Change the cpp color type for uint8_t for now --- api/cpp/cbindgen.rs | 2 +- internal/core/graphics/color.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index 4d22ffcdcd7..078ceedf16a 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -234,7 +234,7 @@ fn default_config() -> cbindgen::Config { // therefore it is ok to reinterpret_cast ("MenuEntryModel".into(), "std::shared_ptr>".into()), ("Coord".into(), "float".into()), - ("Channel".into(), "float".into()), + ("Channel".into(), "uint8_t".into()), ] .iter() .cloned() diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 9f876214902..e41c5f279e8 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -24,9 +24,9 @@ pub struct RgbaColor { pub blue: T, } -#[cfg(not(feature = "8-bit-color"))] +#[cfg(not(any(feature = "8-bit-color", cbindgen)))] type Channel = f32; -#[cfg(feature = "8-bit-color")] +#[cfg(any(feature = "8-bit-color", cbindgen))] type Channel = u8; /// Color represents a color in the Slint run-time, represented using 8-bit channels for From b3760e91633077a2f0b873e325282b1569842d59 Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Tue, 11 Nov 2025 14:22:18 +0100 Subject: [PATCH 14/15] Opt cpp into 8-bit-color --- api/cpp/Cargo.toml | 2 +- internal/core/graphics/color.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/cpp/Cargo.toml b/api/cpp/Cargo.toml index b6901559639..ec188ddd9ee 100644 --- a/api/cpp/Cargo.toml +++ b/api/cpp/Cargo.toml @@ -56,7 +56,7 @@ default = ["std", "backend-winit", "renderer-femtovg", "backend-qt"] i-slint-backend-selector = { workspace = true, optional = true } i-slint-backend-testing = { workspace = true, optional = true, features = ["ffi"] } i-slint-renderer-skia = { workspace = true, features = ["default", "x11", "wayland"], optional = true } -i-slint-core = { workspace = true, features = ["ffi"] } +i-slint-core = { workspace = true, features = ["ffi", "8-bit-color"] } slint-interpreter = { workspace = true, features = ["ffi", "compat-1-2"], optional = true } raw-window-handle = { version = "0.6", optional = true } diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index e41c5f279e8..9f876214902 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -24,9 +24,9 @@ pub struct RgbaColor { pub blue: T, } -#[cfg(not(any(feature = "8-bit-color", cbindgen)))] +#[cfg(not(feature = "8-bit-color"))] type Channel = f32; -#[cfg(any(feature = "8-bit-color", cbindgen))] +#[cfg(feature = "8-bit-color")] type Channel = u8; /// Color represents a color in the Slint run-time, represented using 8-bit channels for From 0d63266055f33cf968f8882ab3762d9e3b0b48eb Mon Sep 17 00:00:00 2001 From: Ashley Ruglys Date: Tue, 11 Nov 2025 17:19:59 +0100 Subject: [PATCH 15/15] Switch feature around --- api/cpp/Cargo.toml | 2 +- internal/core/Cargo.toml | 2 +- internal/core/graphics/color.rs | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/cpp/Cargo.toml b/api/cpp/Cargo.toml index ec188ddd9ee..b6901559639 100644 --- a/api/cpp/Cargo.toml +++ b/api/cpp/Cargo.toml @@ -56,7 +56,7 @@ default = ["std", "backend-winit", "renderer-femtovg", "backend-qt"] i-slint-backend-selector = { workspace = true, optional = true } i-slint-backend-testing = { workspace = true, optional = true, features = ["ffi"] } i-slint-renderer-skia = { workspace = true, features = ["default", "x11", "wayland"], optional = true } -i-slint-core = { workspace = true, features = ["ffi", "8-bit-color"] } +i-slint-core = { workspace = true, features = ["ffi"] } slint-interpreter = { workspace = true, features = ["ffi", "compat-1-2"], optional = true } raw-window-handle = { version = "0.6", optional = true } diff --git a/internal/core/Cargo.toml b/internal/core/Cargo.toml index fcc6ac91dba..7d4ede41aef 100644 --- a/internal/core/Cargo.toml +++ b/internal/core/Cargo.toml @@ -74,7 +74,7 @@ unstable-wgpu-27 = ["dep:wgpu-27"] tr = ["dep:tr"] -8-bit-color = [] +32-bit-color = [] default = ["std", "unicode"] shared-parley = ["shared-fontique", "dep:parley", "dep:skrifa"] diff --git a/internal/core/graphics/color.rs b/internal/core/graphics/color.rs index 9f876214902..cddbfed14e3 100644 --- a/internal/core/graphics/color.rs +++ b/internal/core/graphics/color.rs @@ -24,9 +24,9 @@ pub struct RgbaColor { pub blue: T, } -#[cfg(not(feature = "8-bit-color"))] +#[cfg(feature = "32-bit-color")] type Channel = f32; -#[cfg(feature = "8-bit-color")] +#[cfg(not(feature = "32-bit-color"))] type Channel = u8; /// Color represents a color in the Slint run-time, represented using 8-bit channels for @@ -106,11 +106,11 @@ impl From> for RgbaColor { impl From for RgbaColor { #[inline] fn from(col: Color) -> Self { - #[cfg(not(feature = "8-bit-color"))] + #[cfg(feature = "32-bit-color")] { Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } } - #[cfg(feature = "8-bit-color")] + #[cfg(not(feature = "32-bit-color"))] { let col: RgbaColor = col.into(); col.into() @@ -121,11 +121,11 @@ impl From for RgbaColor { impl From> for Color { #[inline] fn from(col: RgbaColor) -> Self { - #[cfg(not(feature = "8-bit-color"))] + #[cfg(feature = "32-bit-color")] { Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } } - #[cfg(feature = "8-bit-color")] + #[cfg(not(feature = "32-bit-color"))] { let col: RgbaColor = col.into(); col.into() @@ -136,12 +136,12 @@ impl From> for Color { impl From> for Color { #[inline] fn from(col: RgbaColor) -> Self { - #[cfg(not(feature = "8-bit-color"))] + #[cfg(feature = "32-bit-color")] { let col: RgbaColor = col.into(); col.into() } - #[cfg(feature = "8-bit-color")] + #[cfg(not(feature = "32-bit-color"))] { Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } } @@ -151,12 +151,12 @@ impl From> for Color { impl From for RgbaColor { #[inline] fn from(col: Color) -> Self { - #[cfg(not(feature = "8-bit-color"))] + #[cfg(feature = "32-bit-color")] { let col: RgbaColor = col.into(); col.into() } - #[cfg(feature = "8-bit-color")] + #[cfg(not(feature = "32-bit-color"))] { Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } } @@ -185,7 +185,7 @@ impl Color { /// Construct a color from the alpha, red, green and blue color channel parameters. pub const fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self { - #[cfg(not(feature = "8-bit-color"))] + #[cfg(feature = "32-bit-color")] { Self { red: unquantize(red), @@ -194,7 +194,7 @@ impl Color { alpha: unquantize(alpha), } } - #[cfg(feature = "8-bit-color")] + #[cfg(not(feature = "32-bit-color"))] { Self { red, green, blue, alpha } }