From 35e58a003c740dc6c3dd6f1d06ba5a81b519d363 Mon Sep 17 00:00:00 2001 From: Aiving Date: Thu, 14 Mar 2024 11:36:00 +0600 Subject: [PATCH] chore(argb, rgb, xyz, lab): created structs for them chore(tests): update theme test --- src/dynamic_color/dynamic_scheme.rs | 117 +++------ src/hct.rs | 17 +- src/hct/cam16.rs | 11 +- src/hct/solver.rs | 31 +-- src/lib.rs | 2 - src/quantize/point_provider_lab.rs | 12 +- src/quantize/quantizer_wsmeans.rs | 13 +- src/quantize/quantizer_wu.rs | 108 +++++---- src/scheme.rs | 215 ++++------------- src/score.rs | 2 +- src/temperature.rs | 9 +- src/utils.rs | 1 - src/utils/color.rs | 355 ++++++++++++++++++++-------- src/utils/string.rs | 78 ------ tests/theme.rs | 107 +++++---- 15 files changed, 505 insertions(+), 573 deletions(-) delete mode 100644 src/utils/string.rs diff --git a/src/dynamic_color/dynamic_scheme.rs b/src/dynamic_color/dynamic_scheme.rs index be2fa16..efe4b26 100644 --- a/src/dynamic_color/dynamic_scheme.rs +++ b/src/dynamic_color/dynamic_scheme.rs @@ -6,7 +6,6 @@ use crate::hct::Hct; use crate::palettes::tonal::TonalPalette; use crate::utils::color::Argb; use crate::utils::math::sanitize_degrees_double; -use crate::utils::string::hex_from_argb; use super::material_dynamic_colors::MaterialDynamicColors; use super::variant::Variant; @@ -300,99 +299,47 @@ impl PartialEq for DynamicScheme { impl fmt::Display for DynamicScheme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Scheme {{")?; - writeln!(f, " primary = #{}", hex_from_argb(&self.primary()))?; - writeln!(f, " on_primary = #{}", hex_from_argb(&self.on_primary()))?; + writeln!(f, " primary = {}", self.primary())?; + writeln!(f, " on_primary = {}", self.on_primary())?; + writeln!(f, " primary_container = {}", self.primary_container())?; writeln!( f, - " primary_container = #{}", - hex_from_argb(&self.primary_container()) + " on_primary_container = {}", + self.on_primary_container() )?; + writeln!(f, " secondary = {}", self.secondary())?; + writeln!(f, " on_secondary = {}", self.on_secondary())?; + writeln!(f, " secondary_container = {}", self.secondary_container())?; writeln!( f, - " on_primary_container = #{}", - hex_from_argb(&self.on_primary_container()) + " on_secondary_container = {}", + self.on_secondary_container() )?; - writeln!(f, " secondary = #{}", hex_from_argb(&self.secondary()))?; + writeln!(f, " tertiary = {}", self.tertiary())?; + writeln!(f, " on_tertiary = {}", self.on_tertiary())?; + writeln!(f, " tertiary_container = {}", self.tertiary_container())?; writeln!( f, - " on_secondary = #{}", - hex_from_argb(&self.on_secondary()) - )?; - writeln!( - f, - " secondary_container = #{}", - hex_from_argb(&self.secondary_container()) - )?; - writeln!( - f, - " on_secondary_container = #{}", - hex_from_argb(&self.on_secondary_container()) - )?; - writeln!(f, " tertiary = #{}", hex_from_argb(&self.tertiary()))?; - writeln!(f, " on_tertiary = #{}", hex_from_argb(&self.on_tertiary()))?; - writeln!( - f, - " tertiary_container = #{}", - hex_from_argb(&self.tertiary_container()) - )?; - writeln!( - f, - " on_tertiary_container = #{}", - hex_from_argb(&self.on_tertiary_container()) - )?; - writeln!(f, " error = #{}", hex_from_argb(&self.error()))?; - writeln!(f, " on_error = #{}", hex_from_argb(&self.on_error()))?; - writeln!( - f, - " error_container = #{}", - hex_from_argb(&self.error_container()) - )?; - writeln!( - f, - " on_error_container = #{}", - hex_from_argb(&self.on_error_container()) - )?; - writeln!(f, " background = #{}", hex_from_argb(&self.background()))?; - writeln!( - f, - " on_background = #{}", - hex_from_argb(&self.on_background()) - )?; - writeln!(f, " surface = #{}", hex_from_argb(&self.surface()))?; - writeln!(f, " on_surface = #{}", hex_from_argb(&self.on_surface()))?; - writeln!( - f, - " surface_variant = #{}", - hex_from_argb(&self.surface_variant()) - )?; - writeln!( - f, - " on_surface_variant = #{}", - hex_from_argb(&self.on_surface_variant()) - )?; - writeln!(f, " outline = #{}", hex_from_argb(&self.outline()))?; - writeln!( - f, - " outline_variant = #{}", - hex_from_argb(&self.outline_variant()) - )?; - writeln!(f, " shadow = #{}", hex_from_argb(&self.shadow()))?; - writeln!(f, " scrim = #{}", hex_from_argb(&self.scrim()))?; - writeln!( - f, - " inverse_surface = #{}", - hex_from_argb(&self.inverse_surface()) - )?; - writeln!( - f, - " inverse_on_surface = #{}", - hex_from_argb(&self.inverse_on_surface()) - )?; - writeln!( - f, - " inverse_primary = #{}", - hex_from_argb(&self.inverse_primary()) + " on_tertiary_container = {}", + self.on_tertiary_container() )?; + writeln!(f, " error = {}", self.error())?; + writeln!(f, " on_error = {}", self.on_error())?; + writeln!(f, " error_container = {}", self.error_container())?; + writeln!(f, " on_error_container = {}", self.on_error_container())?; + writeln!(f, " background = {}", self.background())?; + writeln!(f, " on_background = {}", self.on_background())?; + writeln!(f, " surface = {}", self.surface())?; + writeln!(f, " on_surface = {}", self.on_surface())?; + writeln!(f, " surface_variant = {}", self.surface_variant())?; + writeln!(f, " on_surface_variant = {}", self.on_surface_variant())?; + writeln!(f, " outline = {}", self.outline())?; + writeln!(f, " outline_variant = {}", self.outline_variant())?; + writeln!(f, " shadow = {}", self.shadow())?; + writeln!(f, " scrim = {}", self.scrim())?; + writeln!(f, " inverse_surface = {}", self.inverse_surface())?; + writeln!(f, " inverse_on_surface = {}", self.inverse_on_surface())?; + writeln!(f, " inverse_primary = {}", self.inverse_primary())?; writeln!(f, "}}") } } diff --git a/src/hct.rs b/src/hct.rs index 2ea0742..8524460 100644 --- a/src/hct.rs +++ b/src/hct.rs @@ -6,7 +6,6 @@ use core::hash::Hasher; #[cfg(feature = "serde")] use serde::Serialize; -use crate::utils::color::lstar_from_argb; use crate::utils::color::lstar_from_y; use crate::utils::color::Argb; @@ -55,7 +54,7 @@ impl Hct { self._hue = cam16.hue; self._chroma = cam16.chroma; - self._tone = lstar_from_argb(&self._argb); + self._tone = self._argb.as_lstar(); } /// 0 <= [newChroma] <= ? @@ -79,7 +78,7 @@ impl Hct { self._hue = cam16.hue; self._chroma = cam16.chroma; - self._tone = lstar_from_argb(&self._argb); + self._tone = self._argb.as_lstar(); } /// Lightness. Ranges from 0 to 100. @@ -107,7 +106,7 @@ impl Hct { self._hue = cam16.hue; self._chroma = cam16.chroma; - self._tone = lstar_from_argb(&self._argb); + self._tone = self._argb.as_lstar(); } pub fn new(argb: Argb) -> Self { @@ -117,7 +116,7 @@ impl Hct { let _hue = cam16.hue; let _chroma = cam16.chroma; - let _tone = lstar_from_argb(&argb); + let _tone = argb.as_lstar(); Self { _hue, @@ -157,9 +156,9 @@ impl Hct { // 2. Create CAM16 of those Xyz coordinates in default VC. let recast_in_vc = Cam16::from_xyz_in_viewing_conditions( - viewed_in_vc[0], - viewed_in_vc[1], - viewed_in_vc[2], + viewed_in_vc.x, + viewed_in_vc.y, + viewed_in_vc.z, ViewingConditions::standard(), ); @@ -169,7 +168,7 @@ impl Hct { Hct::from( recast_in_vc.hue, recast_in_vc.chroma, - lstar_from_y(viewed_in_vc[1]), + lstar_from_y(viewed_in_vc.y), ) } } diff --git a/src/hct/cam16.rs b/src/hct/cam16.rs index c82f9b3..cd2a129 100644 --- a/src/hct/cam16.rs +++ b/src/hct/cam16.rs @@ -1,7 +1,5 @@ use core::f64::consts::PI; -use crate::utils::color::argb_from_xyz; -use crate::utils::color::xyz_from_argb; use crate::utils::color::Argb; use crate::utils::color::Xyz; @@ -86,10 +84,7 @@ impl Cam16 { viewing_conditions: ViewingConditions, ) -> Cam16 { // Transform Argb int to Xyz - let xyz = xyz_from_argb(&argb); - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; + let Xyz { x, y, z } = Xyz::from(argb); Cam16::from_xyz_in_viewing_conditions(x, y, z, viewing_conditions) } @@ -259,7 +254,7 @@ impl Cam16 { pub fn viewed(&self, viewing_conditions: ViewingConditions) -> Argb { let xyz = self.xyz_in_viewing_conditions(viewing_conditions); - argb_from_xyz(xyz) + xyz.into() } /// Xyz representation of CAM16 seen in [viewing_conditions]. @@ -305,7 +300,7 @@ impl Cam16 { let y = 0.38752654 * r_f + 0.62144744 * g_f - 0.00897398 * b_f; let z = -0.01584150 * r_f - 0.03412294 * g_f + 1.04996444 * b_f; - [x, y, z] + Xyz::new(x, y, z) } } diff --git a/src/hct/solver.rs b/src/hct/solver.rs index 70d73db..12a7645 100644 --- a/src/hct/solver.rs +++ b/src/hct/solver.rs @@ -1,9 +1,8 @@ use core::f64::consts::PI; -use crate::utils::color::argb_from_linrgb; -use crate::utils::color::argb_from_lstar; use crate::utils::color::y_from_lstar; use crate::utils::color::Argb; +use crate::utils::color::LinearRgb; use crate::utils::math::matrix_multiply; use crate::utils::math::sanitize_degrees_double; @@ -604,30 +603,31 @@ impl HctSolver { let r_cscaled = Self::inverse_chromatic_adaptation(r_a); let g_cscaled = Self::inverse_chromatic_adaptation(g_a); let b_cscaled = Self::inverse_chromatic_adaptation(b_a); - let linrgb = matrix_multiply( + let [red, green, blue] = matrix_multiply( [r_cscaled, g_cscaled, b_cscaled], LINRGB_FROM_SCALED_DISCOUNT, ); + let linrgb = LinearRgb { red, green, blue }; // =========================================================== // Operations inlined from Cam16 to avoid repeated calculation // =========================================================== - if linrgb[0] < 0.0 || linrgb[1] < 0.0 || linrgb[2] < 0.0 { - return [0; 4]; + if linrgb.red < 0.0 || linrgb.green < 0.0 || linrgb.blue < 0.0 { + return Argb::default(); } let [k_r, k_g, k_b] = Y_FROM_LINRGB; - let fnj = k_r * linrgb[0] + k_g * linrgb[1] + k_b * linrgb[2]; + let fnj = k_r * linrgb.red + k_g * linrgb.green + k_b * linrgb.blue; if fnj <= 0.0 { - return [0; 4]; + return Argb::default(); } if iteration_round == 4 || (fnj - y).abs() < 0.002 { - if linrgb[0] > 100.01 || linrgb[1] > 100.01 || linrgb[2] > 100.01 { - return [0; 4]; + if linrgb.red > 100.01 || linrgb.green > 100.01 || linrgb.blue > 100.01 { + return Argb::default(); } - return argb_from_linrgb(linrgb); + return linrgb.into(); } // Iterates with Newton method, @@ -635,7 +635,7 @@ impl HctSolver { j = j - (fnj - y) * j / (2.0 * fnj); } - [0; 4] + Argb::default() } /// Finds an sRgb color with the given hue, chroma, and L*, if @@ -648,7 +648,7 @@ impl HctSolver { /// chroma will be maximized. pub fn solve_to_int(hue_degrees: f64, chroma: f64, lstar: f64) -> Argb { if chroma < 0.0001 || !(0.0001..=99.9999).contains(&lstar) { - return argb_from_lstar(lstar); + return Argb::from_lstar(lstar); } let hue_degrees = sanitize_degrees_double(hue_degrees); @@ -658,13 +658,14 @@ impl HctSolver { let exact_answer = Self::find_result_by_j(hue_radians, chroma, y); - if exact_answer != [0; 4] { + if exact_answer != Argb::default() { return exact_answer; } - let linrgb = Self::bisect_to_limit(y, hue_radians); + let [red, green, blue] = Self::bisect_to_limit(y, hue_radians); + let linrgb = LinearRgb { red, green, blue }; - argb_from_linrgb(linrgb) + linrgb.into() } /// Finds a CAM16 object with the given hue, chroma, and L*, if diff --git a/src/lib.rs b/src/lib.rs index d6fc9ae..52ae475 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,6 @@ pub use utils::image::Image; #[cfg(feature = "image")] pub use utils::image::ImageReader; -pub use utils::string::argb_from_hex; -pub use utils::string::hex_from_argb; pub use utils::theme::theme_from_source_color; pub use palettes::core::CorePalette; diff --git a/src/quantize/point_provider_lab.rs b/src/quantize/point_provider_lab.rs index e3a7dc5..7db4494 100644 --- a/src/quantize/point_provider_lab.rs +++ b/src/quantize/point_provider_lab.rs @@ -1,5 +1,3 @@ -use crate::utils::color::argb_from_lab; -use crate::utils::color::lab_from_argb; use crate::utils::color::Argb; use crate::utils::color::Lab; @@ -9,25 +7,21 @@ pub struct PointProviderLab; impl PointProvider for PointProviderLab { fn lab_from_int(&self, argb: &Argb) -> Lab { - lab_from_argb(argb) + (*argb).into() } fn lab_to_int(&self, lab: &Lab) -> Argb { - argb_from_lab(lab) + (*lab).into() } fn distance(&self, one: &Lab, two: &Lab) -> f64 { - let d_l = one[0] - two[0]; - let d_a = one[1] - two[1]; - let d_b = one[2] - two[2]; - // Standard CIE 1976 delta E formula also takes the square root, unneeded // here. This method is used by quantization algorithms to compare distance, // and the relative ordering is the same, with or without a square root. // This relatively minor optimization is helpful because this method is // called at least once for each pixel in an image. - d_l.powi(2) + d_a.powi(2) + d_b.powi(2) + (one.l - two.l).powi(2) + (one.a - two.a).powi(2) + (one.b - two.b).powi(2) } fn new() -> Self { diff --git a/src/quantize/quantizer_wsmeans.rs b/src/quantize/quantizer_wsmeans.rs index cfd9db0..2c69fb7 100644 --- a/src/quantize/quantizer_wsmeans.rs +++ b/src/quantize/quantizer_wsmeans.rs @@ -4,6 +4,7 @@ use std::cmp::Ordering; use crate::quantize::point_provider_lab::PointProviderLab; use crate::utils::color::Argb; +use crate::utils::color::Lab; use crate::utils::random::Random; use super::point_provider::PointProvider; @@ -82,7 +83,7 @@ impl QuantizerWsmeans { }; let mut pixel_to_count: IndexMap = Default::default(); - let mut points: Vec<[f64; 3]> = vec![]; + let mut points: Vec = vec![]; let mut pixels: Vec = vec![]; let mut point_count = 0; @@ -290,16 +291,16 @@ impl QuantizerWsmeans { let count = counts[i]; pixel_count_sums[cluster_index] += count; - component_asums[cluster_index] += point[0] * count as f64; - component_bsums[cluster_index] += point[1] * count as f64; - component_csums[cluster_index] += point[2] * count as f64; + component_asums[cluster_index] += point.l * count as f64; + component_bsums[cluster_index] += point.a * count as f64; + component_csums[cluster_index] += point.b * count as f64; } for i in 0..cluster_count { let count = pixel_count_sums[i]; if count == 0 { - clusters[i] = [0.0, 0.0, 0.0]; + clusters[i] = Lab::default(); continue; } @@ -308,7 +309,7 @@ impl QuantizerWsmeans { let b = component_bsums[i] / count as f64; let c = component_csums[i] / count as f64; - clusters[i] = [a, b, c] + clusters[i] = Lab { l: a, a: b, b: c }; } } diff --git a/src/quantize/quantizer_wu.rs b/src/quantize/quantizer_wu.rs index 5d328b6..7a61776 100644 --- a/src/quantize/quantizer_wu.rs +++ b/src/quantize/quantizer_wu.rs @@ -3,10 +3,6 @@ use core::fmt; use indexmap::IndexMap; -use crate::utils::color::argb_from_rgb; -use crate::utils::color::blue_from_argb; -use crate::utils::color::green_from_argb; -use crate::utils::color::red_from_argb; use crate::utils::color::Argb; use crate::utils::color::Rgb; @@ -84,9 +80,9 @@ impl QuantizerWu { self.moments = [0.0; TOTAL_SIZE]; for (argb, count) in pixels { - let red = red_from_argb(&argb); - let green = green_from_argb(&argb); - let blue = blue_from_argb(&argb); + let red = argb.red; + let green = argb.green; + let blue = argb.blue; let i_r = (red >> BITS_TO_REMOVE) + 1; let i_g = (green >> BITS_TO_REMOVE) + 1; @@ -152,7 +148,7 @@ impl QuantizerWu { pub fn create_boxes(&mut self, max_color_count: usize) -> CreateBoxesResult { self.cubes = vec![ Cube { - pixels: [[0, 0, 0], [0, 0, 0]], + pixels: [Rgb::default(), Rgb::default()], vol: 0 }; max_color_count @@ -160,12 +156,12 @@ impl QuantizerWu { self.cubes[0] = Cube { pixels: [ - [0, 0, 0], - [ + Rgb::default(), + Rgb::new( SIDE_LENGTH as u8 - 1, SIDE_LENGTH as u8 - 1, SIDE_LENGTH as u8 - 1, - ], + ), ], vol: 0, }; @@ -236,7 +232,7 @@ impl QuantizerWu { let g = (Self::volume(cube, &self.moments_g) / weight) as u8; let b = (Self::volume(cube, &self.moments_b) / weight) as u8; - let color = argb_from_rgb([r, g, b]); + let color = Rgb::new(r, g, b).into(); result.insert(color, 0); } @@ -250,7 +246,15 @@ impl QuantizerWu { let dg = Self::volume(cube, &self.moments_g); let db = Self::volume(cube, &self.moments_b); - let [[r0, g0, b0], [r1, g1, b1]] = cube.pixels; + let [Rgb { + red: r0, + green: g0, + blue: b0, + }, Rgb { + red: r1, + green: g1, + blue: b1, + }] = cube.pixels; let xx = self.moments[Self::get_index(r1, g1, b1)] - self.moments[Self::get_index(r1, g1, b0)] @@ -327,28 +331,28 @@ impl QuantizerWu { cut_direction = Direction::Blue; } - two.pixels[1][0] = one.pixels[1][0]; - two.pixels[1][1] = one.pixels[1][1]; - two.pixels[1][2] = one.pixels[1][2]; + two.pixels[1].red = one.pixels[1].red; + two.pixels[1].green = one.pixels[1].green; + two.pixels[1].blue = one.pixels[1].blue; match cut_direction { Direction::Red => { - one.pixels[1][0] = max_rresult.cut_location.unwrap_or_default(); - two.pixels[0][0] = one.pixels[1][0]; - two.pixels[0][1] = one.pixels[0][1]; - two.pixels[0][2] = one.pixels[0][2]; + one.pixels[1].red = max_rresult.cut_location.unwrap_or_default(); + two.pixels[0].red = one.pixels[1].red; + two.pixels[0].green = one.pixels[0].green; + two.pixels[0].blue = one.pixels[0].blue; } Direction::Green => { - one.pixels[1][1] = max_gresult.cut_location.unwrap_or_default(); - two.pixels[0][0] = one.pixels[0][0]; - two.pixels[0][1] = one.pixels[1][1]; - two.pixels[0][2] = one.pixels[0][2]; + one.pixels[1].blue = max_gresult.cut_location.unwrap_or_default(); + two.pixels[0].red = one.pixels[0].red; + two.pixels[0].green = one.pixels[1].green; + two.pixels[0].blue = one.pixels[0].blue; } Direction::Blue => { - one.pixels[1][2] = max_bresult.cut_location.unwrap_or_default(); - two.pixels[0][0] = one.pixels[0][0]; - two.pixels[0][1] = one.pixels[0][1]; - two.pixels[0][2] = one.pixels[1][2]; + one.pixels[1].green = max_bresult.cut_location.unwrap_or_default(); + two.pixels[0].red = one.pixels[0].red; + two.pixels[0].green = one.pixels[0].green; + two.pixels[0].blue = one.pixels[1].blue; } } @@ -430,7 +434,15 @@ impl QuantizerWu { } pub fn volume(cube: &Cube, moment: &[u32]) -> i32 { - let [[r0, g0, b0], [r1, g1, b1]] = cube.pixels; + let [Rgb { + red: r0, + green: g0, + blue: b0, + }, Rgb { + red: r1, + green: g1, + blue: b1, + }] = cube.pixels; (moment[Self::get_index(r1, g1, b1)] as i32) .wrapping_sub(moment[Self::get_index(r1, g1, b0)] as i32) @@ -443,7 +455,15 @@ impl QuantizerWu { } pub fn bottom(cube: &Cube, direction: &Direction, moment: &[u32]) -> i32 { - let [[r0, g0, b0], [r1, g1, b1]] = cube.pixels; + let [Rgb { + red: r0, + green: g0, + blue: b0, + }, Rgb { + red: r1, + green: g1, + blue: b1, + }] = cube.pixels; match direction { Direction::Red => (moment[Self::get_index(r0, g1, b1)] as i32) @@ -465,7 +485,15 @@ impl QuantizerWu { } pub fn top(cube: &Cube, direction: &Direction, position: u8, moment: &[u32]) -> i32 { - let [[r0, g0, b0], [r1, g1, b1]] = cube.pixels; + let [Rgb { + red: r0, + green: g0, + blue: b0, + }, Rgb { + red: r1, + green: g1, + blue: b1, + }] = cube.pixels; match direction { Direction::Red => (moment[Self::get_index(position, g1, b1)] as i32) @@ -510,15 +538,15 @@ pub struct Cube { impl Cube { pub fn r>(&self, pixel: usize) -> T { - self.pixels[pixel][0].into() + self.pixels[pixel].red.into() } pub fn g>(&self, pixel: usize) -> T { - self.pixels[pixel][1].into() + self.pixels[pixel].green.into() } pub fn b>(&self, pixel: usize) -> T { - self.pixels[pixel][2].into() + self.pixels[pixel].blue.into() } } @@ -527,12 +555,12 @@ impl fmt::Display for Cube { write!( f, "Box R {} -> {} G {} -> {} B {} -> {} VOL = {}", - self.pixels[0][0], - self.pixels[0][1], - self.pixels[0][2], - self.pixels[1][0], - self.pixels[1][1], - self.pixels[1][2], + self.pixels[0].red, + self.pixels[0].green, + self.pixels[0].blue, + self.pixels[1].red, + self.pixels[1].green, + self.pixels[1].blue, self.vol ) } diff --git a/src/scheme.rs b/src/scheme.rs index c2d5fc2..4ded4d4 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -9,7 +9,6 @@ use ahash::HashMap; use crate::dynamic_color::dynamic_scheme::DynamicScheme; use crate::utils::color::Argb; -use crate::utils::string::hex_from_argb; pub mod content; pub mod expressive; @@ -77,171 +76,57 @@ pub struct Scheme { impl fmt::Display for Scheme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Scheme") - .field("primary", &format!("#{}", hex_from_argb(&self.primary))) - .field( - "on_primary", - &format!("#{}", hex_from_argb(&self.on_primary)), - ) - .field( - "primary_container", - &format!("#{}", hex_from_argb(&self.primary_container)), - ) - .field( - "on_primary_container", - &format!("#{}", hex_from_argb(&self.on_primary_container)), - ) - .field( - "inverse_primary", - &format!("#{}", hex_from_argb(&self.inverse_primary)), - ) - .field( - "primary_fixed", - &format!("#{}", hex_from_argb(&self.primary_fixed)), - ) - .field( - "primary_fixed_dim", - &format!("#{}", hex_from_argb(&self.primary_fixed_dim)), - ) - .field( - "on_primary_fixed", - &format!("#{}", hex_from_argb(&self.on_primary_fixed)), - ) - .field( - "on_primary_fixed_variant", - &format!("#{}", hex_from_argb(&self.on_primary_fixed_variant)), - ) - .field("secondary", &format!("#{}", hex_from_argb(&self.secondary))) - .field( - "on_secondary", - &format!("#{}", hex_from_argb(&self.on_secondary)), - ) - .field( - "secondary_container", - &format!("#{}", hex_from_argb(&self.secondary_container)), - ) - .field( - "on_secondary_container", - &format!("#{}", hex_from_argb(&self.on_secondary_container)), - ) - .field( - "secondary_fixed", - &format!("#{}", hex_from_argb(&self.secondary_fixed)), - ) - .field( - "secondary_fixed_dim", - &format!("#{}", hex_from_argb(&self.secondary_fixed_dim)), - ) - .field( - "on_secondary_fixed", - &format!("#{}", hex_from_argb(&self.on_secondary_fixed)), - ) + .field("primary", &self.primary) + .field("on_primary", &self.on_primary) + .field("primary_container", &self.primary_container) + .field("on_primary_container", &self.on_primary_container) + .field("inverse_primary", &self.inverse_primary) + .field("primary_fixed", &self.primary_fixed) + .field("primary_fixed_dim", &self.primary_fixed_dim) + .field("on_primary_fixed", &self.on_primary_fixed) + .field("on_primary_fixed_variant", &self.on_primary_fixed_variant) + .field("secondary", &self.secondary) + .field("on_secondary", &self.on_secondary) + .field("secondary_container", &self.secondary_container) + .field("on_secondary_container", &self.on_secondary_container) + .field("secondary_fixed", &self.secondary_fixed) + .field("secondary_fixed_dim", &self.secondary_fixed_dim) + .field("on_secondary_fixed", &self.on_secondary_fixed) .field( "on_secondary_fixed_variant", - &format!("#{}", hex_from_argb(&self.on_secondary_fixed_variant)), - ) - .field("tertiary", &format!("#{}", hex_from_argb(&self.tertiary))) - .field( - "on_tertiary", - &format!("#{}", hex_from_argb(&self.on_tertiary)), - ) - .field( - "tertiary_container", - &format!("#{}", hex_from_argb(&self.tertiary_container)), - ) - .field( - "on_tertiary_container", - &format!("#{}", hex_from_argb(&self.on_tertiary_container)), - ) - .field( - "tertiary_fixed", - &format!("#{}", hex_from_argb(&self.tertiary_fixed)), - ) - .field( - "tertiary_fixed_dim", - &format!("#{}", hex_from_argb(&self.tertiary_fixed_dim)), - ) - .field( - "on_tertiary_fixed", - &format!("#{}", hex_from_argb(&self.on_tertiary_fixed)), - ) - .field( - "on_tertiary_fixed_variant", - &format!("#{}", hex_from_argb(&self.on_tertiary_fixed_variant)), - ) - .field("error", &format!("#{}", hex_from_argb(&self.error))) - .field("on_error", &format!("#{}", hex_from_argb(&self.on_error))) - .field( - "error_container", - &format!("#{}", hex_from_argb(&self.error_container)), - ) - .field( - "on_error_container", - &format!("#{}", hex_from_argb(&self.on_error_container)), - ) - .field( - "surface_dim", - &format!("#{}", hex_from_argb(&self.surface_dim)), - ) - .field("surface", &format!("#{}", hex_from_argb(&self.surface))) - .field( - "surface_bright", - &format!("#{}", hex_from_argb(&self.surface_bright)), - ) - .field( - "surface_container_lowest", - &format!("#{}", hex_from_argb(&self.surface_container_lowest)), - ) - .field( - "surface_container_low", - &format!("#{}", hex_from_argb(&self.surface_container_low)), - ) - .field( - "surface_container", - &format!("#{}", hex_from_argb(&self.surface_container)), - ) - .field( - "surface_container_high", - &format!("#{}", hex_from_argb(&self.surface_container_high)), - ) - .field( - "surface_container_highest", - &format!("#{}", hex_from_argb(&self.surface_container_highest)), - ) - .field( - "on_surface", - &format!("#{}", hex_from_argb(&self.on_surface)), - ) - .field( - "on_surface_variant", - &format!("#{}", hex_from_argb(&self.on_surface_variant)), - ) - .field("outline", &format!("#{}", hex_from_argb(&self.outline))) - .field( - "outline_variant", - &format!("#{}", hex_from_argb(&self.outline_variant)), - ) - .field( - "inverse_surface", - &format!("#{}", hex_from_argb(&self.inverse_surface)), - ) - .field( - "inverse_on_surface", - &format!("#{}", hex_from_argb(&self.inverse_on_surface)), - ) - .field( - "surface_variant", - &format!("#{}", hex_from_argb(&self.surface_variant)), - ) - .field( - "background", - &format!("#{}", hex_from_argb(&self.background)), - ) - .field( - "on_background", - &format!("#{}", hex_from_argb(&self.on_background)), - ) - .field("shadow", &format!("#{}", hex_from_argb(&self.shadow))) - .field("scrim", &format!("#{}", hex_from_argb(&self.scrim))) + &self.on_secondary_fixed_variant, + ) + .field("tertiary", &self.tertiary) + .field("on_tertiary", &self.on_tertiary) + .field("tertiary_container", &self.tertiary_container) + .field("on_tertiary_container", &self.on_tertiary_container) + .field("tertiary_fixed", &self.tertiary_fixed) + .field("tertiary_fixed_dim", &self.tertiary_fixed_dim) + .field("on_tertiary_fixed", &self.on_tertiary_fixed) + .field("on_tertiary_fixed_variant", &self.on_tertiary_fixed_variant) + .field("error", &self.error) + .field("on_error", &self.on_error) + .field("error_container", &self.error_container) + .field("on_error_container", &self.on_error_container) + .field("surface_dim", &self.surface_dim) + .field("surface", &self.surface) + .field("surface_bright", &self.surface_bright) + .field("surface_container_lowest", &self.surface_container_lowest) + .field("surface_container_low", &self.surface_container_low) + .field("surface_container", &self.surface_container) + .field("surface_container_high", &self.surface_container_high) + .field("surface_container_highest", &self.surface_container_highest) + .field("on_surface", &self.on_surface) + .field("on_surface_variant", &self.on_surface_variant) + .field("outline", &self.outline) + .field("outline_variant", &self.outline_variant) + .field("inverse_surface", &self.inverse_surface) + .field("inverse_on_surface", &self.inverse_on_surface) + .field("surface_variant", &self.surface_variant) + .field("background", &self.background) + .field("on_background", &self.on_background) + .field("shadow", &self.shadow) + .field("scrim", &self.scrim) .finish() } } @@ -485,7 +370,7 @@ impl From for HashMap { let map: HashMap = HashMap::from_iter(value); map.into_iter() - .map(|(key, value)| (key, hex_from_argb(&value))) + .map(|(key, value)| (key, value.as_hex())) .collect() } } diff --git a/src/score.rs b/src/score.rs index ca99a34..1a3e987 100644 --- a/src/score.rs +++ b/src/score.rs @@ -50,7 +50,7 @@ impl Score { filter: Option, ) -> Vec { let desired = desired.unwrap_or(4); - let fallback_color_argb = fallback_color_argb.unwrap_or([255, 66, 133, 244]); + let fallback_color_argb = fallback_color_argb.unwrap_or(Argb::new(255, 66, 133, 244)); let filter = filter.unwrap_or(true); // Get the HCT color for each Argb value, while finding the per hue count and // total count. diff --git a/src/temperature.rs b/src/temperature.rs index f443877..978d5a7 100644 --- a/src/temperature.rs +++ b/src/temperature.rs @@ -3,8 +3,9 @@ use core::cmp::Ordering; use core::f64::consts::PI; use crate::hct::Hct; -use crate::utils::color::lab_from_argb; +use crate::utils::color::Lab; use crate::utils::math::sanitize_degrees_double; +use crate::Argb; /// Design utilities using color temperature theory. /// @@ -354,9 +355,9 @@ impl TemperatureCache { /// - Upper bound: -0.52 + (chroma ^ 1.07 / 20). L*a*b* chroma is infinite. /// Assuming max of 130 chroma, 8.61. pub fn raw_temperature(color: Hct) -> f64 { - let lab = lab_from_argb(&color.into()); - let hue = sanitize_degrees_double(lab[2].atan2(lab[1]) * 180.0 / PI); - let chroma = ((lab[1] * lab[1]) + (lab[2] * lab[2])).sqrt(); + let lab = Lab::from(Argb::from(color)); + let hue = sanitize_degrees_double(lab.b.atan2(lab.a) * 180.0 / PI); + let chroma = ((lab.a * lab.a) + (lab.b * lab.b)).sqrt(); -0.5 + 0.02 * chroma.powf(1.07) * (sanitize_degrees_double(hue - 50.0) * PI / 180.0).cos() } diff --git a/src/utils.rs b/src/utils.rs index c0a67e5..7a25b56 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,5 +6,4 @@ pub mod math; pub(crate) mod random; -pub mod string; pub mod theme; diff --git a/src/utils/color.rs b/src/utils/color.rs index 637833d..494a645 100644 --- a/src/utils/color.rs +++ b/src/utils/color.rs @@ -1,3 +1,7 @@ +use core::fmt; +use std::error::Error; +use std::str::FromStr; + use super::math::matrix_multiply; pub const SRGB_TO_XYZ: [[f64; 3]; 3] = [ @@ -20,151 +24,306 @@ pub const XYZ_TO_SRGB: [[f64; 3]; 3] = [ ]; pub const WHITE_POINT_D65: [f64; 3] = [95.047, 100.0, 108.883]; -pub type Rgb = [u8; 3]; -pub type Argb = [u8; 4]; -pub type LinearRgb = [f64; 3]; -pub type Xyz = [f64; 3]; -pub type Lab = [f64; 3]; - -/** Converts a color from Rgb components to Argb format. */ -pub fn argb_from_rgb([r, g, b]: Rgb) -> Argb { - [255, r, g, b] +#[derive(Debug, Default, Clone, Copy)] +pub struct Rgb { + pub red: u8, + pub green: u8, + pub blue: u8, } -/** Converts a color from linear Rgb components to Argb format. */ -pub fn argb_from_linrgb([r, g, b]: LinearRgb) -> Argb { - let r = delinearized(r); - let g = delinearized(g); - let b = delinearized(b); - - argb_from_rgb([r, g, b]) +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Argb { + pub alpha: u8, + pub red: u8, + pub green: u8, + pub blue: u8, } -/** Returns the alpha component of a color in Argb format. */ -pub fn alpha_from_argb([alpha, _, _, _]: Argb) -> u8 { - alpha +#[derive(Debug, Default, Clone, Copy)] +pub struct LinearRgb { + pub red: f64, + pub green: f64, + pub blue: f64, } -/** Returns the red component of a color in Argb format. */ -pub fn red_from_argb([_, red, _, _]: &Argb) -> u8 { - *red +#[derive(Debug, Default, Clone, Copy)] +pub struct Xyz { + pub x: f64, + pub y: f64, + pub z: f64, } -/** Returns the green component of a color in Argb format. */ -pub fn green_from_argb([_, _, green, _]: &Argb) -> u8 { - *green +#[derive(Debug, Default, Clone, Copy)] +pub struct Lab { + pub l: f64, + pub a: f64, + pub b: f64, } -/** Returns the blue component of a color in Argb format. */ -pub fn blue_from_argb([_, _, _, blue]: &Argb) -> u8 { - *blue +/** Converts a color from Rgb components to Argb format. */ +impl From for Argb { + fn from(Rgb { red, green, blue }: Rgb) -> Argb { + Argb { + alpha: 255, + red, + green, + blue, + } + } } -/** Returns whether a color in Argb format is opaque. */ -//pub fn is_opaque(argb: Argb) -> bool { -// alpha_from_argb(argb) >= 255 -// } +/** Converts a color from linear Rgb components to Argb format. */ +impl From for Argb { + fn from(linear: LinearRgb) -> Argb { + let r = delinearized(linear.red); + let g = delinearized(linear.green); + let b = delinearized(linear.blue); + + Rgb::new(r, g, b).into() + } +} /** Converts a color from Argb to Xyz. */ -pub fn argb_from_xyz([x, y, z]: Xyz) -> Argb { - let matrix = XYZ_TO_SRGB; +impl From for Argb { + fn from(Xyz { x, y, z }: Xyz) -> Argb { + let matrix = XYZ_TO_SRGB; - let linear_r = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z; - let linear_g = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z; - let linear_b = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z; + let linear_r = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z; + let linear_g = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z; + let linear_b = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z; - let r = delinearized(linear_r); - let g = delinearized(linear_g); - let b = delinearized(linear_b); + let r = delinearized(linear_r); + let g = delinearized(linear_g); + let b = delinearized(linear_b); - argb_from_rgb([r, g, b]) + Rgb::new(r, g, b).into() + } } /** Converts a color from Xyz to Argb. */ -pub fn xyz_from_argb(argb: &Argb) -> Xyz { - let r = linearized(red_from_argb(argb)); - let g = linearized(green_from_argb(argb)); - let b = linearized(blue_from_argb(argb)); - - matrix_multiply([r, g, b], SRGB_TO_XYZ) +impl From for Xyz { + fn from( + Argb { + alpha: _, + red, + green, + blue, + }: Argb, + ) -> Xyz { + let r = linearized(red); + let g = linearized(green); + let b = linearized(blue); + + let [x, y, z] = matrix_multiply([r, g, b], SRGB_TO_XYZ); + + Xyz { x, y, z } + } } /** Converts a color represented in Lab color space into an Argb integer. */ -pub fn argb_from_lab([l, a, b]: &Lab) -> Argb { - let white_point = WHITE_POINT_D65; +impl From for Argb { + fn from(Lab { l, a, b }: Lab) -> Argb { + let white_point = WHITE_POINT_D65; - let fy = (l + 16.0) / 116.0; - let fx = a / 500.0 + fy; - let fz = fy - b / 200.0; + let fy = (l + 16.0) / 116.0; + let fx = a / 500.0 + fy; + let fz = fy - b / 200.0; - let x_normalized = lab_invf(fx); - let y_normalized = lab_invf(fy); - let z_normalized = lab_invf(fz); + let x_normalized = lab_invf(fx); + let y_normalized = lab_invf(fy); + let z_normalized = lab_invf(fz); - let x = x_normalized * white_point[0]; - let y = y_normalized * white_point[1]; - let z = z_normalized * white_point[2]; + let x = x_normalized * white_point[0]; + let y = y_normalized * white_point[1]; + let z = z_normalized * white_point[2]; - argb_from_xyz([x, y, z]) + Rgb::new(x as u8, y as u8, z as u8).into() + } } - /** * Converts a color from Argb representation to L*a*b* representation. * * @param argb the Argb representation of a color * @return a Lab object representing the color */ -pub fn lab_from_argb(argb: &Argb) -> Lab { - let linear_r = linearized(red_from_argb(argb)); - let linear_g = linearized(green_from_argb(argb)); - let linear_b = linearized(blue_from_argb(argb)); +impl From for Lab { + fn from( + Argb { + alpha: _, + red, + green, + blue, + }: Argb, + ) -> Lab { + let linear_r = linearized(red); + let linear_g = linearized(green); + let linear_b = linearized(blue); + + let matrix = SRGB_TO_XYZ; + + let x = matrix[0][0] * linear_r + matrix[0][1] * linear_g + matrix[0][2] * linear_b; + let y = matrix[1][0] * linear_r + matrix[1][1] * linear_g + matrix[1][2] * linear_b; + let z = matrix[2][0] * linear_r + matrix[2][1] * linear_g + matrix[2][2] * linear_b; + + let white_point = WHITE_POINT_D65; + + let x_normalized = x / white_point[0]; + let y_normalized = y / white_point[1]; + let z_normalized = z / white_point[2]; + + let fx = lab_f(x_normalized); + let fy = lab_f(y_normalized); + let fz = lab_f(z_normalized); + + let l = 116.0 * fy - 16.0; + let a = 500.0 * (fx - fy); + let b = 200.0 * (fy - fz); + + Lab { l, a, b } + } +} + +fn hex_digit_to_rgb(number: u32) -> Rgb { + let r = number >> 16; + let g = (number >> 8) & 0x00FF; + let b = number & 0x0000_00FF; + + Rgb::new(r as u8, g as u8, b as u8) +} + +/// An error returned when parsing a `bool` using [`from_str`] fails +/// +/// [`from_str`]: super::FromStr::from_str +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParseRgbError; + +impl fmt::Display for ParseRgbError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "provided string was not RGB-like".fmt(f) + } +} + +impl Error for ParseRgbError { + fn description(&self) -> &str { + "failed to parse RGB" + } +} + +const HASH: u8 = b'#'; - let matrix = SRGB_TO_XYZ; +impl FromStr for Argb { + type Err = ParseRgbError; - let x = matrix[0][0] * linear_r + matrix[0][1] * linear_g + matrix[0][2] * linear_b; - let y = matrix[1][0] * linear_r + matrix[1][1] * linear_g + matrix[1][2] * linear_b; - let z = matrix[2][0] * linear_r + matrix[2][1] * linear_g + matrix[2][2] * linear_b; + fn from_str(hex: &str) -> Result { + let s = hex.as_bytes(); + let mut buff: [u8; 6] = [0; 6]; + let mut buff_len = 0; - let white_point = WHITE_POINT_D65; + for b in s { + if !b.is_ascii() || buff_len == 6 { + return Err(ParseRgbError); + } - let x_normalized = x / white_point[0]; - let y_normalized = y / white_point[1]; - let z_normalized = z / white_point[2]; + let bl = b.to_ascii_lowercase(); - let fx = lab_f(x_normalized); - let fy = lab_f(y_normalized); - let fz = lab_f(z_normalized); + if bl == HASH { + continue; + } - let l = 116.0 * fy - 16.0; - let a = 500.0 * (fx - fy); - let b = 200.0 * (fy - fz); + if bl.is_ascii_hexdigit() { + buff[buff_len] = bl; + buff_len += 1; + } else { + return Err(ParseRgbError); + } + } - [l, a, b] + if buff_len == 3 { + buff = [buff[0], buff[0], buff[1], buff[1], buff[2], buff[2]]; + } + + let hex_str = core::str::from_utf8(&buff).map_err(|_| ParseRgbError)?; + let hex_digit = u32::from_str_radix(hex_str, 16).map_err(|_| ParseRgbError)?; + + Ok(hex_digit_to_rgb(hex_digit).into()) + } } -/** - * Converts an L* value to an Argb representation. - * - * @param lstar L* in L*a*b* - * @return Argb representation of grayscale color with lightness matching L* - */ -pub fn argb_from_lstar(lstar: f64) -> Argb { - let y = y_from_lstar(lstar); - let component = delinearized(y); +impl Xyz { + pub const fn new(x: f64, y: f64, z: f64) -> Self { + Self { x, y, z } + } +} - argb_from_rgb([component, component, component]) +impl Lab { + pub const fn new(l: f64, a: f64, b: f64) -> Self { + Self { l, a, b } + } } -/** - * Computes the L* value of a color in Argb representation. - * - * @param argb Argb representation of a color - * @return L*, from L*a*b*, coordinate of the color - */ -pub fn lstar_from_argb(argb: &Argb) -> f64 { - let y = xyz_from_argb(argb)[1]; +impl Rgb { + pub const fn new(red: u8, green: u8, blue: u8) -> Self { + Self { red, green, blue } + } +} + +impl Argb { + pub const fn new(alpha: u8, red: u8, green: u8, blue: u8) -> Self { + Self { + alpha, + red, + green, + blue, + } + } + + /** + * Converts an L* value to an Argb representation. + * + * @param lstar L* in L*a*b* + * @return Argb representation of grayscale color with lightness matching L* + */ + pub fn from_lstar(lstar: f64) -> Self { + let y = y_from_lstar(lstar); + let component = delinearized(y); + + Rgb::new(component, component, component).into() + } + + /** + * Computes the L* value of a color in Argb representation. + * + * @param argb Argb representation of a color + * @return L*, from L*a*b*, coordinate of the color + */ + pub fn as_lstar(&self) -> f64 { + 116.0 * lab_f(Xyz::from(*self).y / 100.0) - 16.0 + } - 116.0 * lab_f(y / 100.0) - 16.0 + fn hex(number: u8) -> String { + let string = format!("{:x}", number); + + if string.len() == 1 { + String::from("0") + &string + } else { + string + } + } + + pub fn as_hex(&self) -> String { + format!( + "#{}{}{}", + Argb::hex(self.red), + Argb::hex(self.green), + Argb::hex(self.blue) + ) + } +} + +impl fmt::Display for Argb { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_hex()) + } } /** diff --git a/src/utils/string.rs b/src/utils/string.rs deleted file mode 100644 index 2670521..0000000 --- a/src/utils/string.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::fmt; - -use super::color::blue_from_argb; -use super::color::green_from_argb; -use super::color::red_from_argb; -use super::color::Argb; - -const HASH: u8 = b'#'; - -fn to_hex(n: u8) -> String { - let s = format!("{:x}", n); - - if s.len() == 1 { - String::from("0") + &s - } else { - s - } -} - -pub fn hex_from_argb(argb: &Argb) -> String { - let red = red_from_argb(argb); - let blue = blue_from_argb(argb); - let green = green_from_argb(argb); - - format!("{}{}{}", to_hex(red), to_hex(green), to_hex(blue)) -} - -#[derive(Debug, Clone)] -pub struct ParseError; - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "failed to parse") - } -} - -pub fn argb_from_hex>(hex: T) -> Result { - let hex: String = hex.into(); - let s = hex.as_bytes(); - let mut buff: [u8; 6] = [0; 6]; - let mut buff_len = 0; - - for b in s { - if !b.is_ascii() || buff_len == 6 { - return Err(ParseError); - } - - let bl = b.to_ascii_lowercase(); - - if bl == HASH { - continue; - } - - if bl.is_ascii_hexdigit() { - buff[buff_len] = bl; - buff_len += 1; - } else { - return Err(ParseError); - } - } - - if buff_len == 3 { - buff = [buff[0], buff[0], buff[1], buff[1], buff[2], buff[2]]; - } - - let hex_str = core::str::from_utf8(&buff).map_err(|_| ParseError)?; - let hex_digit = u32::from_str_radix(hex_str, 16).map_err(|_| ParseError)?; - - Ok(hex_digit_to_rgb(hex_digit)) -} - -fn hex_digit_to_rgb(num: u32) -> [u8; 4] { - let r = num >> 16; - let g = (num >> 8) & 0x00FF; - let b = num & 0x0000_00FF; - - [255, r as u8, g as u8, b as u8] -} diff --git a/tests/theme.rs b/tests/theme.rs index 1193438..8daa6b8 100644 --- a/tests/theme.rs +++ b/tests/theme.rs @@ -1,64 +1,67 @@ -use material_colors::argb_from_hex; +use std::str::FromStr; + use material_colors::theme_from_source_color; -use material_colors::utils::string::ParseError; +use material_colors::utils::color::ParseRgbError; +use material_colors::Argb; +use material_colors::Rgb; use material_colors::Scheme; #[test] -fn test_theme() -> Result<(), ParseError> { - let source = argb_from_hex("#AAE5A4")?; +fn test_theme() -> Result<(), ParseRgbError> { + let source = Argb::from_str("#AAE5A4")?; let theme = theme_from_source_color(source, vec![]); assert_eq!( theme.schemes.dark, Scheme { - primary: [255, 160, 211, 154], - on_primary: [255, 9, 57, 16], - primary_container: [255, 35, 80, 36], - on_primary_container: [255, 188, 240, 181], - inverse_primary: [255, 59, 105, 58], - primary_fixed: [255, 188, 240, 181], - primary_fixed_dim: [255, 160, 211, 154], - on_primary_fixed: [255, 0, 34, 4], - on_primary_fixed_variant: [255, 35, 80, 36], - secondary: [255, 185, 204, 180], - on_secondary: [255, 37, 52, 35], - secondary_container: [255, 59, 75, 56], - on_secondary_container: [255, 213, 232, 207], - secondary_fixed: [255, 213, 232, 207], - secondary_fixed_dim: [255, 185, 204, 180], - on_secondary_fixed: [255, 16, 31, 16], - on_secondary_fixed_variant: [255, 59, 75, 56], - tertiary: [255, 161, 207, 212], - on_tertiary: [255, 0, 54, 59], - tertiary_container: [255, 31, 77, 82], - on_tertiary_container: [255, 188, 235, 241], - tertiary_fixed: [255, 188, 235, 241], - tertiary_fixed_dim: [255, 161, 207, 212], - on_tertiary_fixed: [255, 0, 31, 35], - on_tertiary_fixed_variant: [255, 31, 77, 82], - error: [255, 255, 180, 171], - on_error: [255, 105, 0, 5], - error_container: [255, 147, 0, 10], - on_error_container: [255, 255, 218, 214], - surface_dim: [255, 16, 20, 15], - surface: [255, 16, 20, 15], - surface_bright: [255, 54, 58, 52], - surface_container_lowest: [255, 11, 15, 10], - surface_container_low: [255, 24, 29, 23], - surface_container: [255, 28, 33, 27], - surface_container_high: [255, 39, 43, 37], - surface_container_highest: [255, 50, 54, 48], - on_surface: [255, 224, 228, 219], - on_surface_variant: [255, 194, 201, 189], - outline: [255, 140, 147, 136], - outline_variant: [255, 66, 73, 64], - inverse_surface: [255, 224, 228, 219], - inverse_on_surface: [255, 45, 50, 44], - surface_variant: [255, 66, 73, 64], - background: [255, 16, 20, 15], - on_background: [255, 224, 228, 219], - shadow: [255, 0, 0, 0], - scrim: [255, 0, 0, 0], + primary: Rgb::new(160, 211, 154).into(), + on_primary: Rgb::new(9, 57, 16).into(), + primary_container: Rgb::new(35, 80, 36).into(), + on_primary_container: Rgb::new(188, 240, 181).into(), + inverse_primary: Rgb::new(59, 105, 58).into(), + primary_fixed: Rgb::new(188, 240, 181).into(), + primary_fixed_dim: Rgb::new(160, 211, 154).into(), + on_primary_fixed: Rgb::new(0, 34, 4).into(), + on_primary_fixed_variant: Rgb::new(35, 80, 36).into(), + secondary: Rgb::new(185, 204, 180).into(), + on_secondary: Rgb::new(37, 52, 35).into(), + secondary_container: Rgb::new(59, 75, 56).into(), + on_secondary_container: Rgb::new(213, 232, 207).into(), + secondary_fixed: Rgb::new(213, 232, 207).into(), + secondary_fixed_dim: Rgb::new(185, 204, 180).into(), + on_secondary_fixed: Rgb::new(16, 31, 16).into(), + on_secondary_fixed_variant: Rgb::new(59, 75, 56).into(), + tertiary: Rgb::new(161, 207, 212).into(), + on_tertiary: Rgb::new(0, 54, 59).into(), + tertiary_container: Rgb::new(31, 77, 82).into(), + on_tertiary_container: Rgb::new(188, 235, 241).into(), + tertiary_fixed: Rgb::new(188, 235, 241).into(), + tertiary_fixed_dim: Rgb::new(161, 207, 212).into(), + on_tertiary_fixed: Rgb::new(0, 31, 35).into(), + on_tertiary_fixed_variant: Rgb::new(31, 77, 82).into(), + error: Rgb::new(255, 180, 171).into(), + on_error: Rgb::new(105, 0, 5).into(), + error_container: Rgb::new(147, 0, 10).into(), + on_error_container: Rgb::new(255, 218, 214).into(), + surface_dim: Rgb::new(16, 20, 15).into(), + surface: Rgb::new(16, 20, 15).into(), + surface_bright: Rgb::new(54, 58, 52).into(), + surface_container_lowest: Rgb::new(11, 15, 10).into(), + surface_container_low: Rgb::new(24, 29, 23).into(), + surface_container: Rgb::new(28, 33, 27).into(), + surface_container_high: Rgb::new(39, 43, 37).into(), + surface_container_highest: Rgb::new(50, 54, 48).into(), + on_surface: Rgb::new(224, 228, 219).into(), + on_surface_variant: Rgb::new(194, 201, 189).into(), + outline: Rgb::new(140, 147, 136).into(), + outline_variant: Rgb::new(66, 73, 64).into(), + inverse_surface: Rgb::new(224, 228, 219).into(), + inverse_on_surface: Rgb::new(45, 50, 44).into(), + surface_variant: Rgb::new(66, 73, 64).into(), + background: Rgb::new(16, 20, 15).into(), + on_background: Rgb::new(224, 228, 219).into(), + shadow: Rgb::new(0, 0, 0).into(), + scrim: Rgb::new(0, 0, 0).into(), }, );