1use alloc::{format, string::String};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Rgb {
7 pub r: u8,
9 pub g: u8,
11 pub b: u8,
13}
14
15impl Rgb {
16 #[inline]
18 pub const fn new(r: u8, g: u8, b: u8) -> Self {
19 Self { r, g, b }
20 }
21
22 #[inline]
24 pub const fn from_hex(hex: u32) -> Self {
25 Self::new((hex >> 16) as u8, (hex >> 8) as u8, hex as u8)
26 }
27
28 pub fn from_hex_string(hex: String) -> Self {
29 if hex.chars().count() == 8 && hex.starts_with("0x") {
30 let (_, value_string) = hex.split_at(2);
32 let int_val = u64::from_str_radix(value_string, 16);
34 match int_val {
35 Ok(num) => Self::new(
36 ((num & 0xff0000) >> 16) as u8,
37 ((num & 0xff00) >> 8) as u8,
38 (num & 0xff) as u8,
39 ),
40 _ => Self::new(0, 0, 0),
43 }
44 } else {
45 Self::new(0, 0, 0)
48 }
49 }
50
51 pub fn from_f32(r: f32, g: f32, b: f32) -> Self {
53 Self::new(
54 (r.clamp(0.0, 1.0) * 255.0) as u8,
55 (g.clamp(0.0, 1.0) * 255.0) as u8,
56 (b.clamp(0.0, 1.0) * 255.0) as u8,
57 )
58 }
59
60 #[inline]
62 pub const fn gray(x: u8) -> Self {
63 Self::new(x, x, x)
64 }
65
66 pub fn gray_f32(x: f32) -> Self {
68 Self::from_f32(x, x, x)
69 }
70
71 pub fn lerp(&self, other: Self, t: f32) -> Self {
101 let t = t.clamp(0.0, 1.0);
102 self * (1.0 - t) + other * t
103 }
104}
105
106impl From<(u8, u8, u8)> for Rgb {
107 fn from((r, g, b): (u8, u8, u8)) -> Self {
108 Self::new(r, g, b)
109 }
110}
111
112impl From<(f32, f32, f32)> for Rgb {
113 fn from((r, g, b): (f32, f32, f32)) -> Self {
114 Self::from_f32(r, g, b)
115 }
116}
117
118use crate::ANSIColorCode;
119use crate::TargetGround;
120impl ANSIColorCode for Rgb {
121 fn ansi_color_code(&self, target: TargetGround) -> String {
122 format!("{};2;{};{};{}", target.code() + 8, self.r, self.g, self.b)
123 }
124}
125
126const fn rgb_add(lhs: &Rgb, rhs: &Rgb) -> Rgb {
127 Rgb::new(
128 lhs.r.saturating_add(rhs.r),
129 lhs.g.saturating_add(rhs.g),
130 lhs.b.saturating_add(rhs.b),
131 )
132}
133
134const fn rgb_sub(lhs: &Rgb, rhs: &Rgb) -> Rgb {
135 Rgb::new(
136 lhs.r.saturating_sub(rhs.r),
137 lhs.g.saturating_sub(rhs.g),
138 lhs.b.saturating_sub(rhs.b),
139 )
140}
141
142fn rgb_mul_f32(lhs: &Rgb, rhs: &f32) -> Rgb {
143 Rgb::new(
144 (lhs.r as f32 * rhs.clamp(0.0, 1.0)) as u8,
145 (lhs.g as f32 * rhs.clamp(0.0, 1.0)) as u8,
146 (lhs.b as f32 * rhs.clamp(0.0, 1.0)) as u8,
147 )
148}
149
150const fn rgb_negate(rgb: &Rgb) -> Rgb {
151 Rgb::new(255 - rgb.r, 255 - rgb.g, 255 - rgb.b)
152}
153
154impl core::ops::Add<Rgb> for Rgb {
155 type Output = Rgb;
156
157 fn add(self, rhs: Rgb) -> Self::Output {
158 rgb_add(&self, &rhs)
159 }
160}
161
162impl core::ops::Add<&Rgb> for Rgb {
163 type Output = Rgb;
164
165 fn add(self, rhs: &Rgb) -> Self::Output {
166 rgb_add(&self, rhs)
167 }
168}
169
170impl core::ops::Add<Rgb> for &Rgb {
171 type Output = Rgb;
172
173 fn add(self, rhs: Rgb) -> Self::Output {
174 rgb_add(self, &rhs)
175 }
176}
177
178impl core::ops::Add<&Rgb> for &Rgb {
179 type Output = Rgb;
180
181 fn add(self, rhs: &Rgb) -> Self::Output {
182 rgb_add(self, rhs)
183 }
184}
185
186impl core::ops::Sub<Rgb> for Rgb {
187 type Output = Rgb;
188
189 fn sub(self, rhs: Rgb) -> Self::Output {
190 rgb_sub(&self, &rhs)
191 }
192}
193
194impl core::ops::Sub<&Rgb> for Rgb {
195 type Output = Rgb;
196
197 fn sub(self, rhs: &Rgb) -> Self::Output {
198 rgb_sub(&self, rhs)
199 }
200}
201
202impl core::ops::Sub<Rgb> for &Rgb {
203 type Output = Rgb;
204
205 fn sub(self, rhs: Rgb) -> Self::Output {
206 rgb_sub(self, &rhs)
207 }
208}
209
210impl core::ops::Sub<&Rgb> for &Rgb {
211 type Output = Rgb;
212
213 fn sub(self, rhs: &Rgb) -> Self::Output {
214 rgb_sub(self, rhs)
215 }
216}
217
218impl core::ops::Mul<f32> for Rgb {
219 type Output = Rgb;
220
221 fn mul(self, rhs: f32) -> Self::Output {
222 rgb_mul_f32(&self, &rhs)
223 }
224}
225
226impl core::ops::Mul<&f32> for Rgb {
227 type Output = Rgb;
228
229 fn mul(self, rhs: &f32) -> Self::Output {
230 rgb_mul_f32(&self, rhs)
231 }
232}
233
234impl core::ops::Mul<f32> for &Rgb {
235 type Output = Rgb;
236
237 fn mul(self, rhs: f32) -> Self::Output {
238 rgb_mul_f32(self, &rhs)
239 }
240}
241
242impl core::ops::Mul<&f32> for &Rgb {
243 type Output = Rgb;
244
245 fn mul(self, rhs: &f32) -> Self::Output {
246 rgb_mul_f32(self, rhs)
247 }
248}
249
250impl core::ops::Mul<Rgb> for f32 {
251 type Output = Rgb;
252
253 fn mul(self, rhs: Rgb) -> Self::Output {
254 rgb_mul_f32(&rhs, &self)
255 }
256}
257
258impl core::ops::Mul<&Rgb> for f32 {
259 type Output = Rgb;
260
261 fn mul(self, rhs: &Rgb) -> Self::Output {
262 rgb_mul_f32(rhs, &self)
263 }
264}
265
266impl core::ops::Mul<Rgb> for &f32 {
267 type Output = Rgb;
268
269 fn mul(self, rhs: Rgb) -> Self::Output {
270 rgb_mul_f32(&rhs, self)
271 }
272}
273
274impl core::ops::Mul<&Rgb> for &f32 {
275 type Output = Rgb;
276
277 fn mul(self, rhs: &Rgb) -> Self::Output {
278 rgb_mul_f32(rhs, self)
279 }
280}
281
282impl core::ops::Neg for Rgb {
283 type Output = Rgb;
284
285 fn neg(self) -> Self::Output {
286 rgb_negate(&self)
287 }
288}
289
290impl core::ops::Neg for &Rgb {
291 type Output = Rgb;
292
293 fn neg(self) -> Self::Output {
294 rgb_negate(self)
295 }
296}