use crate::properties::InterpolatedPropertyValue;
#[cfg(not(feature = "std"))]
use num_traits::float::Float;
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct RgbaColor<T> {
pub alpha: T,
pub red: T,
pub green: T,
pub blue: T,
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
#[repr(C)]
pub struct Color {
red: u8,
green: u8,
blue: u8,
alpha: u8,
}
impl From<RgbaColor<u8>> for Color {
fn from(col: RgbaColor<u8>) -> Self {
Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha }
}
}
impl From<Color> for RgbaColor<u8> {
fn from(col: Color) -> Self {
RgbaColor { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha }
}
}
impl From<RgbaColor<u8>> for RgbaColor<f32> {
fn from(col: RgbaColor<u8>) -> 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,
}
}
}
impl From<Color> for RgbaColor<f32> {
fn from(col: Color) -> Self {
let u8col: RgbaColor<u8> = col.into();
u8col.into()
}
}
impl From<RgbaColor<f32>> for Color {
fn from(col: RgbaColor<f32>) -> Self {
Self {
red: (col.red * 255.) as u8,
green: (col.green * 255.) as u8,
blue: (col.blue * 255.) as u8,
alpha: (col.alpha * 255.) as u8,
}
}
}
impl Color {
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,
}
}
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)
}
pub const fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self {
Self { red, green, blue, alpha }
}
pub const fn from_rgb_u8(red: u8, green: u8, blue: u8) -> Self {
Self::from_argb_u8(255, red, green, blue)
}
pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self {
RgbaColor { alpha, red, green, blue }.into()
}
pub fn from_rgb_f32(red: f32, green: f32, blue: f32) -> Self {
Self::from_argb_f32(1.0, red, green, blue)
}
pub fn to_argb_u8(&self) -> RgbaColor<u8> {
RgbaColor::from(*self)
}
pub fn to_argb_f32(&self) -> RgbaColor<f32> {
RgbaColor::from(*self)
}
pub fn red(self) -> u8 {
self.red
}
pub fn green(self) -> u8 {
self.green
}
pub fn blue(self) -> u8 {
self.blue
}
pub fn alpha(self) -> u8 {
self.alpha
}
#[must_use]
pub fn brighter(&self, factor: f32) -> Self {
let rgba: RgbaColor<f32> = (*self).into();
let mut hsva: HsvaColor = rgba.into();
hsva.v *= 1. + factor;
let rgba: RgbaColor<f32> = hsva.into();
rgba.into()
}
#[must_use]
pub fn darker(&self, factor: f32) -> Self {
let rgba: RgbaColor<f32> = (*self).into();
let mut hsva: HsvaColor = rgba.into();
hsva.v /= 1. + factor;
let rgba: RgbaColor<f32> = hsva.into();
rgba.into()
}
}
impl InterpolatedPropertyValue for Color {
fn interpolate(&self, target_value: &Self, t: f32) -> Self {
Self {
red: self.red.interpolate(&target_value.red, t),
green: self.green.interpolate(&target_value.green, t),
blue: self.blue.interpolate(&target_value.blue, t),
alpha: self.alpha.interpolate(&target_value.alpha, t),
}
}
}
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)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
struct HsvaColor {
h: f32,
s: f32,
v: f32,
alpha: f32,
}
impl From<RgbaColor<f32>> for HsvaColor {
fn from(col: RgbaColor<f32>) -> Self {
let red = col.red;
let green = col.green;
let blue = col.blue;
let min = red.min(green).min(blue);
let max = red.max(green).max(blue);
let chroma = max - min;
#[allow(clippy::float_cmp)] let hue = 60.
* if chroma == 0. {
0.0
} else if max == red {
((green - blue) / chroma) % 6.0
} else if max == green {
2. + (blue - red) / chroma
} else {
4. + (red - green) / chroma
};
let saturation = if max == 0. { 0. } else { chroma / max };
Self { h: hue, s: saturation, v: max, alpha: col.alpha }
}
}
impl From<HsvaColor> for RgbaColor<f32> {
fn from(col: HsvaColor) -> Self {
let chroma = col.s * col.v;
let x = chroma * (1. - ((col.h / 60.) % 2. - 1.).abs());
let (red, green, blue) = match (col.h / 60.0) as usize {
0 => (chroma, x, 0.),
1 => (x, chroma, 0.),
2 => (0., chroma, x),
3 => (0., x, chroma),
4 => (x, 0., chroma),
5 => (chroma, 0., x),
_ => (0., 0., 0.),
};
let m = col.v - chroma;
Self { red: red + m, green: green + m, blue: blue + m, alpha: col.alpha }
}
}
#[test]
fn test_rgb_to_hsv() {
assert_eq!(
HsvaColor::from(RgbaColor::<f32> { red: 1., green: 1., blue: 1., alpha: 0.5 }),
HsvaColor { h: 0., s: 0., v: 1., alpha: 0.5 }
);
assert_eq!(
RgbaColor::<f32>::from(HsvaColor { h: 0., s: 0., v: 1., alpha: 0.3 }),
RgbaColor::<f32> { red: 1., green: 1., blue: 1., alpha: 0.3 }
);
assert_eq!(
HsvaColor::from(RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 }),
HsvaColor { h: 120., s: 1., v: 0.9, alpha: 1.0 }
);
assert_eq!(
RgbaColor::<f32>::from(HsvaColor { h: 120., s: 1., v: 0.9, alpha: 1.0 }),
RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 }
);
}
#[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));
}
#[cfg(feature = "ffi")]
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::*;
#[no_mangle]
pub unsafe extern "C" fn slint_color_brighter(col: &Color, factor: f32, out: *mut Color) {
core::ptr::write(out, col.brighter(factor))
}
#[no_mangle]
pub unsafe extern "C" fn slint_color_darker(col: &Color, factor: f32, out: *mut Color) {
core::ptr::write(out, col.darker(factor))
}
}