1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct Rgb {
5 pub r: u8,
7 pub g: u8,
9 pub b: u8,
11}
12
13impl Rgb {
14 #[inline]
16 pub const fn new(r: u8, g: u8, b: u8) -> Self {
17 Self { r, g, b }
18 }
19
20 #[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 let (_, value_string) = hex.split_at(2);
30 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 _ => Self::new(0, 0, 0),
41 }
42 } else {
43 Self::new(0, 0, 0)
46 }
47 }
48
49 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 #[inline]
60 pub const fn gray(x: u8) -> Self {
61 Self::new(x, x, x)
62 }
63
64 pub fn gray_f32(x: f32) -> Self {
66 Self::from_f32(x, x, x)
67 }
68
69 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, g, b): (u8, u8, u8)) -> Self {
106 Self::new(r, g, b)
107 }
108}
109
110impl From<(f32, f32, f32)> for Rgb {
111 fn from((r, g, b): (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 lhs.r.saturating_add(rhs.r),
127 lhs.g.saturating_add(rhs.g),
128 lhs.b.saturating_add(rhs.b),
129 )
130}
131
132const fn rgb_sub(lhs: &Rgb, rhs: &Rgb) -> Rgb {
133 Rgb::new(
134 lhs.r.saturating_sub(rhs.r),
135 lhs.g.saturating_sub(rhs.g),
136 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(0.0, 1.0)) as u8,
145 )
146}
147
148const fn rgb_negate(rgb: &Rgb) -> Rgb {
149 Rgb::new(255 - rgb.r, 255 - rgb.g, 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(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(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}