1 | // Code liberally borrowed from here |
2 | // https://github.com/navierr/coloriz |
3 | use std::ops; |
4 | use std::u32; |
5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
6 | pub struct Rgb { |
7 | /// Red |
8 | pub r: u8, |
9 | /// Green |
10 | pub g: u8, |
11 | /// Blue |
12 | pub b: u8, |
13 | } |
14 | |
15 | impl 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 | |
106 | impl 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 | |
112 | impl 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 | |
118 | use crate::ANSIColorCode; |
119 | use crate::TargetGround; |
120 | impl 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 | |
126 | overload::overload!( |
127 | (lhs: ?Rgb) + (rhs: ?Rgb) -> Rgb { |
128 | Rgb::new( |
129 | lhs.r.saturating_add(rhs.r), |
130 | lhs.g.saturating_add(rhs.g), |
131 | lhs.b.saturating_add(rhs.b) |
132 | ) |
133 | } |
134 | ); |
135 | |
136 | overload::overload!( |
137 | (lhs: ?Rgb) - (rhs: ?Rgb) -> Rgb { |
138 | Rgb::new( |
139 | lhs.r.saturating_sub(rhs.r), |
140 | lhs.g.saturating_sub(rhs.g), |
141 | lhs.b.saturating_sub(rhs.b) |
142 | ) |
143 | } |
144 | ); |
145 | |
146 | overload::overload!( |
147 | (lhs: ?Rgb) * (rhs: ?f32) -> Rgb { |
148 | Rgb::new( |
149 | (lhs.r as f32 * rhs.clamp(0.0, 1.0)) as u8, |
150 | (lhs.g as f32 * rhs.clamp(0.0, 1.0)) as u8, |
151 | (lhs.b as f32 * rhs.clamp(0.0, 1.0)) as u8 |
152 | ) |
153 | } |
154 | ); |
155 | |
156 | overload::overload!( |
157 | (lhs: ?f32) * (rhs: ?Rgb) -> Rgb { |
158 | Rgb::new( |
159 | (rhs.r as f32 * lhs.clamp(0.0, 1.0)) as u8, |
160 | (rhs.g as f32 * lhs.clamp(0.0, 1.0)) as u8, |
161 | (rhs.b as f32 * lhs.clamp(0.0, 1.0)) as u8 |
162 | ) |
163 | } |
164 | ); |
165 | |
166 | overload::overload!( |
167 | -(rgb: ?Rgb) -> Rgb { |
168 | Rgb::new( |
169 | 255 - rgb.r, |
170 | 255 - rgb.g, |
171 | 255 - rgb.b) |
172 | } |
173 | ); |
174 | |