1// Code liberally borrowed from here
2// https://github.com/navierr/coloriz
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct Rgb {
5 /// Red
6 pub r: u8,
7 /// Green
8 pub g: u8,
9 /// Blue
10 pub b: u8,
11}
12
13impl Rgb {
14 /// Creates a new [Rgb] color
15 #[inline]
16 pub const fn new(r: u8, g: u8, b: u8) -> Self {
17 Self { r, g, b }
18 }
19
20 /// Creates a new [Rgb] color with a hex code
21 #[inline]
22 pub const fn from_hex(hex: u32) -> Self {
23 Self::new((hex >> 16) as u8, (hex >> 8) as u8, hex as u8)
24 }
25
26 pub fn from_hex_string(hex: String) -> Self {
27 if hex.chars().count() == 8 && hex.starts_with("0x") {
28 // eprintln!("hex:{:?}", hex);
29 let (_, value_string) = hex.split_at(2);
30 // eprintln!("value_string:{:?}", value_string);
31 let int_val = u64::from_str_radix(value_string, 16);
32 match int_val {
33 Ok(num) => Self::new(
34 ((num & 0xff0000) >> 16) as u8,
35 ((num & 0xff00) >> 8) as u8,
36 (num & 0xff) as u8,
37 ),
38 // Don't fail, just make the color black
39 // Should we fail?
40 _ => Self::new(0, 0, 0),
41 }
42 } else {
43 // Don't fail, just make the color black.
44 // Should we fail?
45 Self::new(0, 0, 0)
46 }
47 }
48
49 /// Creates a new [Rgb] color with three [f32] values
50 pub fn from_f32(r: f32, g: f32, b: f32) -> Self {
51 Self::new(
52 (r.clamp(0.0, 1.0) * 255.0) as u8,
53 (g.clamp(0.0, 1.0) * 255.0) as u8,
54 (b.clamp(0.0, 1.0) * 255.0) as u8,
55 )
56 }
57
58 /// Creates a grayscale [Rgb] color
59 #[inline]
60 pub const fn gray(x: u8) -> Self {
61 Self::new(x, x, x)
62 }
63
64 /// Creates a grayscale [Rgb] color with a [f32] value
65 pub fn gray_f32(x: f32) -> Self {
66 Self::from_f32(x, x, x)
67 }
68
69 /// Creates a new [Rgb] color from a [HSL] color
70 // pub fn from_hsl(hsl: HSL) -> Self {
71 // if hsl.s == 0.0 {
72 // return Self::gray_f32(hsl.l);
73 // }
74
75 // let q = if hsl.l < 0.5 {
76 // hsl.l * (1.0 + hsl.s)
77 // } else {
78 // hsl.l + hsl.s - hsl.l * hsl.s
79 // };
80 // let p = 2.0 * hsl.l - q;
81 // let h2c = |t: f32| {
82 // let t = t.clamp(0.0, 1.0);
83 // if 6.0 * t < 1.0 {
84 // p + 6.0 * (q - p) * t
85 // } else if t < 0.5 {
86 // q
87 // } else if 1.0 < 1.5 * t {
88 // p + 6.0 * (q - p) * (1.0 / 1.5 - t)
89 // } else {
90 // p
91 // }
92 // };
93
94 // Self::from_f32(h2c(hsl.h + 1.0 / 3.0), h2c(hsl.h), h2c(hsl.h - 1.0 / 3.0))
95 // }
96
97 /// Computes the linear interpolation between `self` and `other` for `t`
98 pub fn lerp(&self, other: Self, t: f32) -> Self {
99 let t = t.clamp(0.0, 1.0);
100 self * (1.0 - t) + other * t
101 }
102}
103
104impl From<(u8, u8, u8)> for Rgb {
105 fn from((r: u8, g: u8, b: u8): (u8, u8, u8)) -> Self {
106 Self::new(r, g, b)
107 }
108}
109
110impl From<(f32, f32, f32)> for Rgb {
111 fn from((r: f32, g: f32, b: f32): (f32, f32, f32)) -> Self {
112 Self::from_f32(r, g, b)
113 }
114}
115
116use crate::ANSIColorCode;
117use crate::TargetGround;
118impl ANSIColorCode for Rgb {
119 fn ansi_color_code(&self, target: TargetGround) -> String {
120 format!("{};2;{};{};{}", target.code() + 8, self.r, self.g, self.b)
121 }
122}
123
124const fn rgb_add(lhs: &Rgb, rhs: &Rgb) -> Rgb {
125 Rgb::new(
126 r:lhs.r.saturating_add(rhs.r),
127 g:lhs.g.saturating_add(rhs.g),
128 b:lhs.b.saturating_add(rhs.b),
129 )
130}
131
132const fn rgb_sub(lhs: &Rgb, rhs: &Rgb) -> Rgb {
133 Rgb::new(
134 r:lhs.r.saturating_sub(rhs.r),
135 g:lhs.g.saturating_sub(rhs.g),
136 b:lhs.b.saturating_sub(rhs.b),
137 )
138}
139
140fn rgb_mul_f32(lhs: &Rgb, rhs: &f32) -> Rgb {
141 Rgb::new(
142 (lhs.r as f32 * rhs.clamp(0.0, 1.0)) as u8,
143 (lhs.g as f32 * rhs.clamp(0.0, 1.0)) as u8,
144 (lhs.b as f32 * rhs.clamp(min:0.0, max:1.0)) as u8,
145 )
146}
147
148const fn rgb_negate(rgb: &Rgb) -> Rgb {
149 Rgb::new(r:255 - rgb.r, g:255 - rgb.g, b:255 - rgb.b)
150}
151
152impl std::ops::Add<Rgb> for Rgb {
153 type Output = Rgb;
154
155 fn add(self, rhs: Rgb) -> Self::Output {
156 rgb_add(&self, &rhs)
157 }
158}
159
160impl std::ops::Add<&Rgb> for Rgb {
161 type Output = Rgb;
162
163 fn add(self, rhs: &Rgb) -> Self::Output {
164 rgb_add(&self, rhs)
165 }
166}
167
168impl std::ops::Add<Rgb> for &Rgb {
169 type Output = Rgb;
170
171 fn add(self, rhs: Rgb) -> Self::Output {
172 rgb_add(self, &rhs)
173 }
174}
175
176impl std::ops::Add<&Rgb> for &Rgb {
177 type Output = Rgb;
178
179 fn add(self, rhs: &Rgb) -> Self::Output {
180 rgb_add(self, rhs)
181 }
182}
183
184impl std::ops::Sub<Rgb> for Rgb {
185 type Output = Rgb;
186
187 fn sub(self, rhs: Rgb) -> Self::Output {
188 rgb_sub(&self, &rhs)
189 }
190}
191
192impl std::ops::Sub<&Rgb> for Rgb {
193 type Output = Rgb;
194
195 fn sub(self, rhs: &Rgb) -> Self::Output {
196 rgb_sub(&self, rhs)
197 }
198}
199
200impl std::ops::Sub<Rgb> for &Rgb {
201 type Output = Rgb;
202
203 fn sub(self, rhs: Rgb) -> Self::Output {
204 rgb_sub(self, &rhs)
205 }
206}
207
208impl std::ops::Sub<&Rgb> for &Rgb {
209 type Output = Rgb;
210
211 fn sub(self, rhs: &Rgb) -> Self::Output {
212 rgb_sub(self, rhs)
213 }
214}
215
216impl std::ops::Mul<f32> for Rgb {
217 type Output = Rgb;
218
219 fn mul(self, rhs: f32) -> Self::Output {
220 rgb_mul_f32(&self, &rhs)
221 }
222}
223
224impl std::ops::Mul<&f32> for Rgb {
225 type Output = Rgb;
226
227 fn mul(self, rhs: &f32) -> Self::Output {
228 rgb_mul_f32(&self, rhs)
229 }
230}
231
232impl std::ops::Mul<f32> for &Rgb {
233 type Output = Rgb;
234
235 fn mul(self, rhs: f32) -> Self::Output {
236 rgb_mul_f32(self, &rhs)
237 }
238}
239
240impl std::ops::Mul<&f32> for &Rgb {
241 type Output = Rgb;
242
243 fn mul(self, rhs: &f32) -> Self::Output {
244 rgb_mul_f32(self, rhs)
245 }
246}
247
248impl std::ops::Mul<Rgb> for f32 {
249 type Output = Rgb;
250
251 fn mul(self, rhs: Rgb) -> Self::Output {
252 rgb_mul_f32(&rhs, &self)
253 }
254}
255
256impl std::ops::Mul<&Rgb> for f32 {
257 type Output = Rgb;
258
259 fn mul(self, rhs: &Rgb) -> Self::Output {
260 rgb_mul_f32(lhs:rhs, &self)
261 }
262}
263
264impl std::ops::Mul<Rgb> for &f32 {
265 type Output = Rgb;
266
267 fn mul(self, rhs: Rgb) -> Self::Output {
268 rgb_mul_f32(&rhs, self)
269 }
270}
271
272impl std::ops::Mul<&Rgb> for &f32 {
273 type Output = Rgb;
274
275 fn mul(self, rhs: &Rgb) -> Self::Output {
276 rgb_mul_f32(lhs:rhs, self)
277 }
278}
279
280impl std::ops::Neg for Rgb {
281 type Output = Rgb;
282
283 fn neg(self) -> Self::Output {
284 rgb_negate(&self)
285 }
286}
287
288impl std::ops::Neg for &Rgb {
289 type Output = Rgb;
290
291 fn neg(self) -> Self::Output {
292 rgb_negate(self)
293 }
294}
295