1#![feature(decl_macro)]
4
5use colorsys::Hsl;
6use lab::Lab;
7
8pub mod constants;
9pub mod color;
10mod traits;
11
12pub use self::constants::*;
13pub use self::color::*;
14pub use self::traits::*;
15
16pub use rgb;
17pub use rgb::{Rgb, Rgba, RGB8, RGBA8};
18
19pub const fn normalize_rgb (Rgb { r, g, b } : RGB8) -> Rgb <f32> {
21 Rgb::new (
22 r as f32 / 255.0,
23 g as f32 / 255.0,
24 b as f32 / 255.0
25 )
26}
27
28pub const fn normalize_rgba (Rgba { r, g, b, a } : RGBA8) -> Rgba <f32> {
30 Rgba::new (
31 r as f32 / 255.0,
32 g as f32 / 255.0,
33 b as f32 / 255.0,
34 a as f32 / 255.0
35 )
36}
37
38#[expect(clippy::cast_possible_truncation)]
40#[expect(clippy::cast_sign_loss)]
41pub const fn quantize_rgb (Rgb {r, g, b } : Rgb <f32>) -> RGB8 {
42 Rgb::new (
43 (r * 255.0) as u8,
44 (g * 255.0) as u8,
45 (b * 255.0) as u8
46 )
47}
48
49#[expect(clippy::cast_possible_truncation)]
51#[expect(clippy::cast_sign_loss)]
52pub const fn quantize_rgba (Rgba { r, g, b, a } : Rgba <f32>) -> RGBA8 {
53 Rgba::new (
54 (r * 255.0) as u8,
55 (g * 255.0) as u8,
56 (b * 255.0) as u8,
57 (a * 255.0) as u8
58 )
59}
60
61pub fn hue_deg (rgb : RGB8) -> Option <f32> {
63 let Rgb { r, g, b } = normalize_rgb (rgb);
64 let max = f32::max (f32::max (r, g), b);
65 let min = f32::min (f32::min (r, g), b);
66 let delta = max - min;
67 if delta == 0.0 {
68 return None
69 }
70 let mut hue;
71 if max == r {
72 hue = (g - b) / delta % 6.0
73 } else if max == g {
74 hue = ((b - r) / delta) + 2.0;
75 } else {
76 hue = ((r - g) / delta) + 4.0;
77 }
78 hue *= 60.0;
79 if hue < 0.0 {
80 hue += 360.0;
81 }
82 Some (hue)
83}
84
85pub fn hue_luminance_custom (hue_deg : f32, luminance : f32) -> RGB8 {
90 let mut rgb = hue_to_rgb (hue_deg);
91 loop {
92 let lum = luminance_custom (rgb);
93 let diff = lum - luminance;
94 if diff.abs() < 0.25 {
95 return rgb
96 }
97 let mut hsl = Hsl::from (colorsys::Rgb::from (rgb.into_array()));
98 let new_lightness = if lum > luminance {
99 hsl.lightness() - 1.0
100 } else {
101 debug_assert!(lum < luminance);
102 hsl.lightness() + 1.0
103 };
104 hsl.set_lightness (new_lightness);
105 let array : [u8; 3] = colorsys::Rgb::from (hsl).into();
106 rgb = Rgb::from (array);
107 }
108}
109
110pub fn hue_to_rgb (hue : f32) -> RGB8 {
112 let h = hue.rem_euclid (360.0);
113 let c = 1.0; let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
115 let [r1, g1, b1] = match h {
116 h if h < 60.0 => [c, x, 0.0],
117 h if h < 120.0 => [x, c, 0.0],
118 h if h < 180.0 => [0.0, c, x],
119 h if h < 240.0 => [0.0, x, c],
120 h if h < 300.0 => [x, 0.0, c],
121 _ => [c, 0.0, x]
122 };
123 quantize_rgb (Rgb::new (r1, g1, b1))
124}
125
126#[expect(clippy::cast_possible_truncation)]
130pub fn luminance_custom (Rgb { r, g, b } : RGB8) -> f32 {
131 if r == g && g == b {
132 return (r as f32 / 255.0) * 100.0
133 }
134 let tmp_lab = Lab::from_rgb (&[r, g, b]);
136 let tmp_rgb = Lab { a: 0.0, b: 0.0, .. tmp_lab }.to_rgb();
138 let lum_lab_a = ((tmp_lab.a / 127.0) * 50.0) / 10.0;
141 let lum_lab_b = (((tmp_lab.a - tmp_lab.b) / 127.0) * 50.0) / 10.0 / 2.0;
143 let mut lum_lab_ab = lum_lab_a + lum_lab_b;
145 if tmp_lab.a > tmp_lab.b {
147 lum_lab_ab += (((tmp_lab.a - tmp_lab.b) / 127.0) * 50.0) / 10.0;
150 }
151 Hsl::from (colorsys::Rgb::from (&tmp_rgb)).lightness() as f32 + lum_lab_ab
154}
155
156pub fn report_sizes() {
157 use std::mem::size_of;
158 macro_rules! show {
159 ($e:expr) => { println!("{}: {:?}", stringify!($e), $e); }
160 }
161 println!("report sizes...");
162 show!(size_of::<Color>());
163 println!("...report sizes");
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn hue() {
172 for hue in 0..360 {
173 let rgb = hue_to_rgb (hue as f32);
174 let hue_deg = hue_deg (rgb).unwrap();
175 assert!((hue as f32 - hue_deg).abs() < 1.0,
176 "rgb: {rgb:?}, hue: {hue}, hue_deg: {hue_deg}");
177 }
178 }
179
180 #[test]
181 fn hue_lum() {
182 assert_eq!([211, 0, 0], hue_luminance_custom (0.0, 44.0).into_array());
183 assert_eq!([121, 121, 0], hue_luminance_custom (60.0, 44.0).into_array());
184 assert_eq!([0, 145, 0], hue_luminance_custom (120.0, 44.0).into_array());
185 assert_eq!([0, 130, 130], hue_luminance_custom (180.0, 44.0).into_array());
186 assert_eq!([0, 0, 255], hue_luminance_custom (240.0, 44.0).into_array());
187 assert_eq!([159, 0, 159], hue_luminance_custom (300.0, 44.0).into_array());
188 for hue in 0..360 {
190 for luminance in 0..100 {
191 let _rgb = hue_luminance_custom (hue as f32, luminance as f32);
192 }
193 }
194 }
195}