nu_ansi_term/
rgb.rs

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, 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}