nu_ansi_term/
rgb.rs

1use alloc::{format, string::String};
2
3// Code liberally borrowed from here
4// https://github.com/navierr/coloriz
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Rgb {
7    /// Red
8    pub r: u8,
9    /// Green
10    pub g: u8,
11    /// Blue
12    pub b: u8,
13}
14
15impl Rgb {
16    /// Creates a new [Rgb] color
17    #[inline]
18    pub const fn new(r: u8, g: u8, b: u8) -> Self {
19        Self { r, g, b }
20    }
21
22    /// Creates a new [Rgb] color with a hex code
23    #[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            // eprintln!("hex:{:?}", hex);
31            let (_, value_string) = hex.split_at(2);
32            // eprintln!("value_string:{:?}", value_string);
33            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                // Don't fail, just make the color black
41                // Should we fail?
42                _ => Self::new(0, 0, 0),
43            }
44        } else {
45            // Don't fail, just make the color black.
46            // Should we fail?
47            Self::new(0, 0, 0)
48        }
49    }
50
51    /// Creates a new [Rgb] color with three [f32] values
52    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    /// Creates a grayscale [Rgb] color
61    #[inline]
62    pub const fn gray(x: u8) -> Self {
63        Self::new(x, x, x)
64    }
65
66    /// Creates a grayscale [Rgb] color with a [f32] value
67    pub fn gray_f32(x: f32) -> Self {
68        Self::from_f32(x, x, x)
69    }
70
71    // Creates a new [Rgb] color from a [HSL] color
72    // pub fn from_hsl(hsl: HSL) -> Self {
73    //     if hsl.s == 0.0 {
74    //         return Self::gray_f32(hsl.l);
75    //     }
76
77    //     let q = if hsl.l < 0.5 {
78    //         hsl.l * (1.0 + hsl.s)
79    //     } else {
80    //         hsl.l + hsl.s - hsl.l * hsl.s
81    //     };
82    //     let p = 2.0 * hsl.l - q;
83    //     let h2c = |t: f32| {
84    //         let t = t.clamp(0.0, 1.0);
85    //         if 6.0 * t < 1.0 {
86    //             p + 6.0 * (q - p) * t
87    //         } else if t < 0.5 {
88    //             q
89    //         } else if 1.0 < 1.5 * t {
90    //             p + 6.0 * (q - p) * (1.0 / 1.5 - t)
91    //         } else {
92    //             p
93    //         }
94    //     };
95
96    //     Self::from_f32(h2c(hsl.h + 1.0 / 3.0), h2c(hsl.h), h2c(hsl.h - 1.0 / 3.0))
97    // }
98
99    /// Computes the linear interpolation between `self` and `other` for `t`
100    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}