├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── bacon.toml ├── doc ├── ansi-mix.png ├── ansi-variations.png ├── dark-to-light.png ├── faded-background.png └── rgb-to-ansi.png ├── examples ├── ansi-grey │ └── main.rs ├── ansi-mix │ └── main.rs ├── ansi-variations │ └── main.rs └── rgb-to-ansi │ └── main.rs ├── features.md └── src ├── ansi.rs ├── color.rs ├── error.rs ├── hsl.rs ├── lib.rs └── rgb.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .bacon-locations 2 | /glassbench_*.db 3 | Cargo.lock 4 | /target/ 5 | /trav 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coolor" 3 | version = "1.1.0" 4 | edition = "2021" 5 | authors = ["dystroy "] 6 | repository = "https://github.com/Canop/coolor" 7 | description = "conversion between color formats" 8 | keywords = ["ansi", "hsl", "rgb", "color", "no-std"] 9 | categories = ["no-std"] 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | [features] 14 | default = [] 15 | 16 | [dependencies] 17 | crossterm = { optional=true, version="0.29" } 18 | 19 | [dev-dependencies] 20 | crossterm = { version="0.29" } 21 | rand = { version = "0.9", features = ["std_rng"] } 22 | 23 | [[example]] 24 | name = "rgb-to-ansi" 25 | required-features = ["crossterm"] 26 | 27 | [[example]] 28 | name = "ansi-grey" 29 | required-features = ["crossterm"] 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Canop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MIT][s2]][l2] [![Latest Version][s1]][l1] [![docs][s3]][l3] [![Chat on Miaou][s4]][l4] 2 | 3 | [s1]: https://img.shields.io/crates/v/coolor.svg 4 | [l1]: https://crates.io/crates/coolor 5 | 6 | [s2]: https://img.shields.io/badge/license-MIT-blue.svg 7 | [l2]: LICENSE 8 | 9 | [s3]: https://docs.rs/coolor/badge.svg 10 | [l3]: https://docs.rs/coolor/ 11 | 12 | [s4]: https://miaou.dystroy.org/static/shields/room.svg 13 | [l4]: https://miaou.dystroy.org/3 14 | 15 | Definition of ANSI, RGB and HSL color types and all the conversions between them. 16 | 17 | There are many other color conversion crates. 18 | This one is no-std and pure-rust and may be useful when you're interested into 19 | 20 | - variations of an ANSI color for your TUI application, for example fading, lightening, darkening, with compatibility with terminals that don't support RGB. 21 | - translations of color schemes 22 | - automatic downgrading of RGB color schemes for non RGB terminals 23 | - automated building of harmonious color schemes with guarantees of contrast 24 | - etc. 25 | 26 | Coolor is used in [SafeCloset](https://github.com/Canop/safecloset) to dynamically fade the background behind a dialog: 27 | 28 | ![img](doc/faded-background.png) 29 | 30 | Be warned that the ANSI range is intrinsically limited to 240 colors and that not all intuitive operations will give good results. 31 | 32 | Coolor doesn't contain functions to print on the terminal, but the colors can be used in other crates. 33 | There's an optional [feature](features.md) for conversion from and into [Crossterm](https://github.com/crossterm-rs/crossterm) colors. You'll see it used in the included examples. 34 | 35 | ## Included Examples 36 | 37 | ### ansi-variations 38 | 39 | Luminosity and saturation variations of all 240 ANSI colors, with all variants still ANSI colors. 40 | 41 | ![ansi-variations](doc/ansi-variations.png) 42 | 43 | ### ansi-mix 44 | 45 | Several ways to mix colors 46 | 47 | ![ansi-mix](doc/ansi-mix.png) 48 | 49 | ### rgb-to-ansi 50 | 51 | Finding the nearest ANSI color from a RGB one 52 | 53 | ![rgb-to-ansi](doc/rgb-to-ansi.png) 54 | -------------------------------------------------------------------------------- /bacon.toml: -------------------------------------------------------------------------------- 1 | # This is a configuration file for the bacon tool 2 | # More info at https://github.com/Canop/bacon 3 | 4 | default_job = "check-all" 5 | 6 | [jobs] 7 | 8 | [jobs.check] 9 | command = ["cargo", "check", "--color", "always"] 10 | need_stdout = false 11 | 12 | [jobs.check-all] 13 | command = [ 14 | "cargo", "check", 15 | "--all-targets", 16 | "--features", "crossterm", 17 | "--color", "always", 18 | ] 19 | need_stdout = false 20 | watch = ["tests", "benches", "examples"] 21 | 22 | [jobs.ex] 23 | command = [ 24 | "cargo", "run", 25 | "--color", "always", 26 | "--features", "crossterm", 27 | "--example" 28 | ] 29 | allow_warnings = true 30 | need_stdout = true 31 | 32 | [jobs.clippy-all] 33 | command = [ 34 | "cargo", "clippy", 35 | "--color", "always", 36 | "--", 37 | "-A", "clippy::match_like_matches_macro", 38 | "-A", "clippy::collapsible_if", 39 | "-A", "clippy::collapsible_else_if", 40 | "-A", "clippy::manual_range_contains", 41 | "-A", "clippy::collapsible_else_if", 42 | ] 43 | need_stdout = false 44 | 45 | [jobs.test] 46 | command = ["cargo", "test", "--color", "always", "--features", "crossterm"] 47 | need_stdout = true 48 | watch = ["tests"] 49 | 50 | [jobs.doc] 51 | command = ["cargo", "doc", "--color", "always", "--no-deps"] 52 | need_stdout = false 53 | -------------------------------------------------------------------------------- /doc/ansi-mix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/coolor/5931e3336b6fa27193da955117e70a4587b6df8a/doc/ansi-mix.png -------------------------------------------------------------------------------- /doc/ansi-variations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/coolor/5931e3336b6fa27193da955117e70a4587b6df8a/doc/ansi-variations.png -------------------------------------------------------------------------------- /doc/dark-to-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/coolor/5931e3336b6fa27193da955117e70a4587b6df8a/doc/dark-to-light.png -------------------------------------------------------------------------------- /doc/faded-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/coolor/5931e3336b6fa27193da955117e70a4587b6df8a/doc/faded-background.png -------------------------------------------------------------------------------- /doc/rgb-to-ansi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/coolor/5931e3336b6fa27193da955117e70a4587b6df8a/doc/rgb-to-ansi.png -------------------------------------------------------------------------------- /examples/ansi-grey/main.rs: -------------------------------------------------------------------------------- 1 | //! Run this with 2 | //! cargo run --example ansi-grey --features=crossterm 3 | use { 4 | coolor::*, 5 | crossterm::style::{Color as CC, Stylize}, 6 | }; 7 | 8 | /// Return the hexa notation commonly used for the web 9 | pub fn to_hexa(rgb: Rgb) -> String { 10 | format!("#{:02X}{:02X}{:02X}", rgb.r, rgb.g, rgb.b) 11 | } 12 | 13 | fn print_color(c: Color) { 14 | let cc: CC = c.into(); 15 | print!("{}", "██████".with(cc)); 16 | } 17 | 18 | fn show(rgb: Rgb, ansi: AnsiColor) { 19 | print!(" {} ", to_hexa(rgb)); 20 | print_color(ansi.into()); 21 | print!(" {:>3} {:>3}", ansi.code, rgb.r); 22 | println!(); 23 | } 24 | 25 | fn main() { 26 | println!("All the ANSI colors with r=g=b"); 27 | println!(" hexa ansi r=g=b"); 28 | for code in 0..=255 { 29 | let ansi = AnsiColor { code }; 30 | let rgb = ansi.to_rgb(); 31 | if rgb.r == rgb.g && rgb.g == rgb.b { 32 | show(rgb, ansi); 33 | } 34 | } 35 | println!(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/ansi-mix/main.rs: -------------------------------------------------------------------------------- 1 | //! Run this with 2 | //! cargo run --example ansi-mix 3 | use { 4 | coolor::*, 5 | crossterm::style::{self, Stylize}, 6 | rand::Rng, 7 | }; 8 | 9 | /// number of steps in gradients 10 | const N: usize = 20; 11 | 12 | fn rand_ansi() -> AnsiColor { 13 | AnsiColor::new(rand::rng().random()) 14 | } 15 | 16 | fn print_ansi(ansi: AnsiColor) { 17 | print!("{}", "█".with(style::Color::AnsiValue(ansi.code))); 18 | } 19 | 20 | fn bar(n: usize) -> String { 21 | "─".repeat(n) 22 | } 23 | 24 | fn main() { 25 | const T1: &str = "HSL walk"; 26 | const T2: &str = "RGB walk"; 27 | const T3: &str = "blend"; 28 | let b = bar(N + 2); 29 | let b = &b; 30 | println!( 31 | "\n{:^w$}", 32 | "Blending random ANSI colors, all variants still ANSI", 33 | w = 3 * N + 18 34 | ); 35 | println!(" ┌─────┬─────┬{}┬{}┬{}┐", b, b, b); 36 | println!(" │ src │ dst │{:^w$}│{:^w$}│{:^w$}│", T1, T2, T3, w = N + 2); 37 | println!(" ├─────┼─────┼{}┼{}┼{}┤", b, b, b); 38 | for _ in 0..20 { 39 | let c1 = rand_ansi(); 40 | let c2 = rand_ansi(); 41 | print!(" │ "); 42 | for _ in 0..3 { 43 | print_ansi(c1); 44 | } 45 | print!(" │ "); 46 | for _ in 0..3 { 47 | print_ansi(c2); 48 | } 49 | print!(" │ "); 50 | let hsl1 = c1.to_hsl(); 51 | let hsl2 = c2.to_hsl(); 52 | let n = N - 1; 53 | for i in 0..=n { 54 | let hsl = Hsl::mix(hsl1, (n - i) as f32, hsl2, i as f32); 55 | print_ansi(hsl.to_ansi()); 56 | } 57 | print!(" │ "); 58 | let rgb1 = c1.to_rgb(); 59 | let rgb2 = c2.to_rgb(); 60 | for i in 0..=n { 61 | let rgb = Rgb::mix(rgb1, (n - i) as f32, rgb2, i as f32); 62 | print_ansi(rgb.to_ansi()); 63 | } 64 | print!(" │ "); 65 | for i in 0..=n { 66 | let c = Color::blend(c1, (n - i) as f32, c2, i as f32); 67 | print_ansi(c.ansi()); 68 | } 69 | print!(" │ "); 70 | println!(); 71 | } 72 | println!(" └─────┴─────┴{}┴{}┴{}┘\n", b, b, b); 73 | } 74 | -------------------------------------------------------------------------------- /examples/ansi-variations/main.rs: -------------------------------------------------------------------------------- 1 | //! Run this with 2 | //! cargo run --example ansi-variations 3 | use { 4 | coolor::AnsiColor, 5 | crossterm::style::{Color, Stylize}, 6 | }; 7 | 8 | fn print_color(ansi: AnsiColor) { 9 | print!("{}", "█".with(Color::AnsiValue(ansi.code))); 10 | } 11 | 12 | fn main() { 13 | println!(" Variations on ANSI colors:"); 14 | println!(" ┌──────┬────────┬─────────────────┬─────────────┐"); 15 | println!(" │ code │ normal │ luminosity │ saturation │"); 16 | println!(" ├──────┼────────┼─────────────────┼─────────────┤"); 17 | for code in 1..=255 { 18 | let ansi = AnsiColor::new(code); 19 | print!(" │ {:>3} │ ", code); 20 | for _ in 0..6 { 21 | print_color(ansi); 22 | } 23 | print!(" │ "); 24 | for i in -7..=7 { 25 | print_color(ansi.with_luminosity_change((i as f32) * 0.1)); 26 | } 27 | print!(" │ "); 28 | for i in -10..=0 { 29 | print_color(ansi.with_saturation_change((i as f32) * 0.1)); 30 | } 31 | println!(" │"); 32 | } 33 | println!(" └──────┴────────┴─────────────────┴─────────────┘"); 34 | } 35 | -------------------------------------------------------------------------------- /examples/rgb-to-ansi/main.rs: -------------------------------------------------------------------------------- 1 | //! Run this with 2 | //! cargo run --features crossterm --example rgb-to-ansi 3 | use { 4 | coolor::*, 5 | crossterm::style::{Color as CC, Stylize}, 6 | rand::Rng, 7 | }; 8 | 9 | /// Return the hexa notation commonly used for the web 10 | pub fn to_hexa(rgb: Rgb) -> String { 11 | format!("#{:02X}{:02X}{:02X}", rgb.r, rgb.g, rgb.b) 12 | } 13 | 14 | fn print_color(c: Color) { 15 | let cc: CC = c.into(); 16 | print!("{}", "██████".with(cc)); 17 | } 18 | 19 | fn compare(rgb: Rgb) { 20 | print!(" {} ", to_hexa(rgb)); 21 | print_color(rgb.into()); 22 | let ansi = rgb.to_ansi(); 23 | print_color(ansi.into()); 24 | print!(" {:>3}", ansi.code); 25 | println!(); 26 | } 27 | 28 | fn main() { 29 | let mut rng = rand::rng(); 30 | println!(" Print some random RGB colors and the nearest ANSI color"); 31 | println!(" rgb ansi"); 32 | for _ in 0..20 { 33 | let rgb = Rgb::new(rng.random(), rng.random(), rng.random()); 34 | compare(rgb); 35 | } 36 | println!(); 37 | } 38 | -------------------------------------------------------------------------------- /features.md: -------------------------------------------------------------------------------- 1 | 2 | Optional features in the Coolor crate: 3 | 4 | ## crossterm 5 | 6 | This feature enables From/Into conversions between the coolor Color type and the [Crossterm](https://github.com/crossterm-rs/crossterm) Color type. 7 | 8 | Example inclusion in Cargo.toml: 9 | 10 | ```toml 11 | [dependencies] 12 | coolor = { version="1", features=["crossterm"] } 13 | ``` 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/ansi.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// 8-bit Ansi Color Code 4 | /// 5 | /// See 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 7 | pub struct AnsiColor { 8 | pub code: u8, 9 | } 10 | 11 | impl AnsiColor { 12 | pub const fn new(code: u8) -> Self { 13 | Self { code } 14 | } 15 | pub const fn to_hsl(self) -> Hsl { 16 | ANSI_TO_HSL[self.code as usize] 17 | } 18 | #[inline(always)] 19 | pub const fn to_rgb(self) -> Rgb { 20 | ANSI_TO_RGB[self.code as usize] 21 | } 22 | pub fn with_luminosity_change(self, delta_luminosity: f32) -> Self { 23 | let mut hsl = self.to_hsl(); 24 | hsl.l = (hsl.l + delta_luminosity).clamp(0.0, 1.0); 25 | hsl.to_ansi() 26 | } 27 | pub fn with_luminosity(self, l: f32) -> Self { 28 | let mut hsl = self.to_hsl(); 29 | hsl.l = l; 30 | hsl.to_ansi() 31 | } 32 | pub fn with_saturation_change(self, delta_saturation: f32) -> Self { 33 | let mut hsl = self.to_hsl(); 34 | hsl.s = (hsl.s + delta_saturation).clamp(0.0, 1.0); 35 | hsl.to_ansi() 36 | } 37 | pub fn with_saturation(self, s: f32) -> Self { 38 | let mut hsl = self.to_hsl(); 39 | hsl.s = s; 40 | hsl.to_ansi() 41 | } 42 | } 43 | 44 | pub const ANSI_TO_RGB: &[Rgb] = &[ 45 | Rgb { 46 | r: 0x00, 47 | g: 0x00, 48 | b: 0x00, 49 | }, 50 | Rgb { 51 | r: 0x80, 52 | g: 0x00, 53 | b: 0x00, 54 | }, 55 | Rgb { 56 | r: 0x00, 57 | g: 0x80, 58 | b: 0x00, 59 | }, 60 | Rgb { 61 | r: 0x80, 62 | g: 0x80, 63 | b: 0x00, 64 | }, 65 | Rgb { 66 | r: 0x00, 67 | g: 0x00, 68 | b: 0x80, 69 | }, 70 | Rgb { 71 | r: 0x80, 72 | g: 0x00, 73 | b: 0x80, 74 | }, 75 | Rgb { 76 | r: 0x00, 77 | g: 0x80, 78 | b: 0x80, 79 | }, 80 | Rgb { 81 | r: 0xC0, 82 | g: 0xC0, 83 | b: 0xC0, 84 | }, 85 | Rgb { 86 | r: 0x80, 87 | g: 0x80, 88 | b: 0x80, 89 | }, 90 | Rgb { 91 | r: 0xFF, 92 | g: 0x00, 93 | b: 0x00, 94 | }, 95 | Rgb { 96 | r: 0x00, 97 | g: 0xFF, 98 | b: 0x00, 99 | }, 100 | Rgb { 101 | r: 0xFF, 102 | g: 0xFF, 103 | b: 0x00, 104 | }, 105 | Rgb { 106 | r: 0x00, 107 | g: 0x00, 108 | b: 0xFF, 109 | }, 110 | Rgb { 111 | r: 0xFF, 112 | g: 0x00, 113 | b: 0xFF, 114 | }, 115 | Rgb { 116 | r: 0x00, 117 | g: 0xFF, 118 | b: 0xFF, 119 | }, 120 | Rgb { 121 | r: 0xFF, 122 | g: 0xFF, 123 | b: 0xFF, 124 | }, 125 | Rgb { 126 | r: 0x00, 127 | g: 0x00, 128 | b: 0x00, 129 | }, 130 | Rgb { 131 | r: 0x00, 132 | g: 0x00, 133 | b: 0x5F, 134 | }, 135 | Rgb { 136 | r: 0x00, 137 | g: 0x00, 138 | b: 0x87, 139 | }, 140 | Rgb { 141 | r: 0x00, 142 | g: 0x00, 143 | b: 0xAF, 144 | }, 145 | Rgb { 146 | r: 0x00, 147 | g: 0x00, 148 | b: 0xD7, 149 | }, 150 | Rgb { 151 | r: 0x00, 152 | g: 0x00, 153 | b: 0xFF, 154 | }, 155 | Rgb { 156 | r: 0x00, 157 | g: 0x5F, 158 | b: 0x00, 159 | }, 160 | Rgb { 161 | r: 0x00, 162 | g: 0x5F, 163 | b: 0x5F, 164 | }, 165 | Rgb { 166 | r: 0x00, 167 | g: 0x5F, 168 | b: 0x87, 169 | }, 170 | Rgb { 171 | r: 0x00, 172 | g: 0x5F, 173 | b: 0xAF, 174 | }, 175 | Rgb { 176 | r: 0x00, 177 | g: 0x5F, 178 | b: 0xD7, 179 | }, 180 | Rgb { 181 | r: 0x00, 182 | g: 0x5F, 183 | b: 0xFF, 184 | }, 185 | Rgb { 186 | r: 0x00, 187 | g: 0x87, 188 | b: 0x00, 189 | }, 190 | Rgb { 191 | r: 0x00, 192 | g: 0x87, 193 | b: 0x5F, 194 | }, 195 | Rgb { 196 | r: 0x00, 197 | g: 0x87, 198 | b: 0x87, 199 | }, 200 | Rgb { 201 | r: 0x00, 202 | g: 0x87, 203 | b: 0xAF, 204 | }, 205 | Rgb { 206 | r: 0x00, 207 | g: 0x87, 208 | b: 0xD7, 209 | }, 210 | Rgb { 211 | r: 0x00, 212 | g: 0x87, 213 | b: 0xFF, 214 | }, 215 | Rgb { 216 | r: 0x00, 217 | g: 0xAF, 218 | b: 0x00, 219 | }, 220 | Rgb { 221 | r: 0x00, 222 | g: 0xAF, 223 | b: 0x5F, 224 | }, 225 | Rgb { 226 | r: 0x00, 227 | g: 0xAF, 228 | b: 0x87, 229 | }, 230 | Rgb { 231 | r: 0x00, 232 | g: 0xAF, 233 | b: 0xAF, 234 | }, 235 | Rgb { 236 | r: 0x00, 237 | g: 0xAF, 238 | b: 0xD7, 239 | }, 240 | Rgb { 241 | r: 0x00, 242 | g: 0xAF, 243 | b: 0xFF, 244 | }, 245 | Rgb { 246 | r: 0x00, 247 | g: 0xD7, 248 | b: 0x00, 249 | }, 250 | Rgb { 251 | r: 0x00, 252 | g: 0xD7, 253 | b: 0x5F, 254 | }, 255 | Rgb { 256 | r: 0x00, 257 | g: 0xD7, 258 | b: 0x87, 259 | }, 260 | Rgb { 261 | r: 0x00, 262 | g: 0xD7, 263 | b: 0xAF, 264 | }, 265 | Rgb { 266 | r: 0x00, 267 | g: 0xD7, 268 | b: 0xD7, 269 | }, 270 | Rgb { 271 | r: 0x00, 272 | g: 0xD7, 273 | b: 0xFF, 274 | }, 275 | Rgb { 276 | r: 0x00, 277 | g: 0xFF, 278 | b: 0x00, 279 | }, 280 | Rgb { 281 | r: 0x00, 282 | g: 0xFF, 283 | b: 0x5F, 284 | }, 285 | Rgb { 286 | r: 0x00, 287 | g: 0xFF, 288 | b: 0x87, 289 | }, 290 | Rgb { 291 | r: 0x00, 292 | g: 0xFF, 293 | b: 0xAF, 294 | }, 295 | Rgb { 296 | r: 0x00, 297 | g: 0xFF, 298 | b: 0xD7, 299 | }, 300 | Rgb { 301 | r: 0x00, 302 | g: 0xFF, 303 | b: 0xFF, 304 | }, 305 | Rgb { 306 | r: 0x5F, 307 | g: 0x00, 308 | b: 0x00, 309 | }, 310 | Rgb { 311 | r: 0x5F, 312 | g: 0x00, 313 | b: 0x5F, 314 | }, 315 | Rgb { 316 | r: 0x5F, 317 | g: 0x00, 318 | b: 0x87, 319 | }, 320 | Rgb { 321 | r: 0x5F, 322 | g: 0x00, 323 | b: 0xAF, 324 | }, 325 | Rgb { 326 | r: 0x5F, 327 | g: 0x00, 328 | b: 0xD7, 329 | }, 330 | Rgb { 331 | r: 0x5F, 332 | g: 0x00, 333 | b: 0xFF, 334 | }, 335 | Rgb { 336 | r: 0x5F, 337 | g: 0x5F, 338 | b: 0x00, 339 | }, 340 | Rgb { 341 | r: 0x5F, 342 | g: 0x5F, 343 | b: 0x5F, 344 | }, 345 | Rgb { 346 | r: 0x5F, 347 | g: 0x5F, 348 | b: 0x87, 349 | }, 350 | Rgb { 351 | r: 0x5F, 352 | g: 0x5F, 353 | b: 0xAF, 354 | }, 355 | Rgb { 356 | r: 0x5F, 357 | g: 0x5F, 358 | b: 0xD7, 359 | }, 360 | Rgb { 361 | r: 0x5F, 362 | g: 0x5F, 363 | b: 0xFF, 364 | }, 365 | Rgb { 366 | r: 0x5F, 367 | g: 0x87, 368 | b: 0x00, 369 | }, 370 | Rgb { 371 | r: 0x5F, 372 | g: 0x87, 373 | b: 0x5F, 374 | }, 375 | Rgb { 376 | r: 0x5F, 377 | g: 0x87, 378 | b: 0x87, 379 | }, 380 | Rgb { 381 | r: 0x5F, 382 | g: 0x87, 383 | b: 0xAF, 384 | }, 385 | Rgb { 386 | r: 0x5F, 387 | g: 0x87, 388 | b: 0xD7, 389 | }, 390 | Rgb { 391 | r: 0x5F, 392 | g: 0x87, 393 | b: 0xFF, 394 | }, 395 | Rgb { 396 | r: 0x5F, 397 | g: 0xAF, 398 | b: 0x00, 399 | }, 400 | Rgb { 401 | r: 0x5F, 402 | g: 0xAF, 403 | b: 0x5F, 404 | }, 405 | Rgb { 406 | r: 0x5F, 407 | g: 0xAF, 408 | b: 0x87, 409 | }, 410 | Rgb { 411 | r: 0x5F, 412 | g: 0xAF, 413 | b: 0xAF, 414 | }, 415 | Rgb { 416 | r: 0x5F, 417 | g: 0xAF, 418 | b: 0xD7, 419 | }, 420 | Rgb { 421 | r: 0x5F, 422 | g: 0xAF, 423 | b: 0xFF, 424 | }, 425 | Rgb { 426 | r: 0x5F, 427 | g: 0xD7, 428 | b: 0x00, 429 | }, 430 | Rgb { 431 | r: 0x5F, 432 | g: 0xD7, 433 | b: 0x5F, 434 | }, 435 | Rgb { 436 | r: 0x5F, 437 | g: 0xD7, 438 | b: 0x87, 439 | }, 440 | Rgb { 441 | r: 0x5F, 442 | g: 0xD7, 443 | b: 0xAF, 444 | }, 445 | Rgb { 446 | r: 0x5F, 447 | g: 0xD7, 448 | b: 0xD7, 449 | }, 450 | Rgb { 451 | r: 0x5F, 452 | g: 0xD7, 453 | b: 0xFF, 454 | }, 455 | Rgb { 456 | r: 0x5F, 457 | g: 0xFF, 458 | b: 0x00, 459 | }, 460 | Rgb { 461 | r: 0x5F, 462 | g: 0xFF, 463 | b: 0x5F, 464 | }, 465 | Rgb { 466 | r: 0x5F, 467 | g: 0xFF, 468 | b: 0x87, 469 | }, 470 | Rgb { 471 | r: 0x5F, 472 | g: 0xFF, 473 | b: 0xAF, 474 | }, 475 | Rgb { 476 | r: 0x5F, 477 | g: 0xFF, 478 | b: 0xD7, 479 | }, 480 | Rgb { 481 | r: 0x5F, 482 | g: 0xFF, 483 | b: 0xFF, 484 | }, 485 | Rgb { 486 | r: 0x87, 487 | g: 0x00, 488 | b: 0x00, 489 | }, 490 | Rgb { 491 | r: 0x87, 492 | g: 0x00, 493 | b: 0x5F, 494 | }, 495 | Rgb { 496 | r: 0x87, 497 | g: 0x00, 498 | b: 0x87, 499 | }, 500 | Rgb { 501 | r: 0x87, 502 | g: 0x00, 503 | b: 0xAF, 504 | }, 505 | Rgb { 506 | r: 0x87, 507 | g: 0x00, 508 | b: 0xD7, 509 | }, 510 | Rgb { 511 | r: 0x87, 512 | g: 0x00, 513 | b: 0xFF, 514 | }, 515 | Rgb { 516 | r: 0x87, 517 | g: 0x5F, 518 | b: 0x00, 519 | }, 520 | Rgb { 521 | r: 0x87, 522 | g: 0x5F, 523 | b: 0x5F, 524 | }, 525 | Rgb { 526 | r: 0x87, 527 | g: 0x5F, 528 | b: 0x87, 529 | }, 530 | Rgb { 531 | r: 0x87, 532 | g: 0x5F, 533 | b: 0xAF, 534 | }, 535 | Rgb { 536 | r: 0x87, 537 | g: 0x5F, 538 | b: 0xD7, 539 | }, 540 | Rgb { 541 | r: 0x87, 542 | g: 0x5F, 543 | b: 0xFF, 544 | }, 545 | Rgb { 546 | r: 0x87, 547 | g: 0x87, 548 | b: 0x00, 549 | }, 550 | Rgb { 551 | r: 0x87, 552 | g: 0x87, 553 | b: 0x5F, 554 | }, 555 | Rgb { 556 | r: 0x87, 557 | g: 0x87, 558 | b: 0x87, 559 | }, 560 | Rgb { 561 | r: 0x87, 562 | g: 0x87, 563 | b: 0xAF, 564 | }, 565 | Rgb { 566 | r: 0x87, 567 | g: 0x87, 568 | b: 0xD7, 569 | }, 570 | Rgb { 571 | r: 0x87, 572 | g: 0x87, 573 | b: 0xFF, 574 | }, 575 | Rgb { 576 | r: 0x87, 577 | g: 0xAF, 578 | b: 0x00, 579 | }, 580 | Rgb { 581 | r: 0x87, 582 | g: 0xAF, 583 | b: 0x5F, 584 | }, 585 | Rgb { 586 | r: 0x87, 587 | g: 0xAF, 588 | b: 0x87, 589 | }, 590 | Rgb { 591 | r: 0x87, 592 | g: 0xAF, 593 | b: 0xAF, 594 | }, 595 | Rgb { 596 | r: 0x87, 597 | g: 0xAF, 598 | b: 0xD7, 599 | }, 600 | Rgb { 601 | r: 0x87, 602 | g: 0xAF, 603 | b: 0xFF, 604 | }, 605 | Rgb { 606 | r: 0x87, 607 | g: 0xD7, 608 | b: 0x00, 609 | }, 610 | Rgb { 611 | r: 0x87, 612 | g: 0xD7, 613 | b: 0x5F, 614 | }, 615 | Rgb { 616 | r: 0x87, 617 | g: 0xD7, 618 | b: 0x87, 619 | }, 620 | Rgb { 621 | r: 0x87, 622 | g: 0xD7, 623 | b: 0xAF, 624 | }, 625 | Rgb { 626 | r: 0x87, 627 | g: 0xD7, 628 | b: 0xD7, 629 | }, 630 | Rgb { 631 | r: 0x87, 632 | g: 0xD7, 633 | b: 0xFF, 634 | }, 635 | Rgb { 636 | r: 0x87, 637 | g: 0xFF, 638 | b: 0x00, 639 | }, 640 | Rgb { 641 | r: 0x87, 642 | g: 0xFF, 643 | b: 0x5F, 644 | }, 645 | Rgb { 646 | r: 0x87, 647 | g: 0xFF, 648 | b: 0x87, 649 | }, 650 | Rgb { 651 | r: 0x87, 652 | g: 0xFF, 653 | b: 0xAF, 654 | }, 655 | Rgb { 656 | r: 0x87, 657 | g: 0xFF, 658 | b: 0xD7, 659 | }, 660 | Rgb { 661 | r: 0x87, 662 | g: 0xFF, 663 | b: 0xFF, 664 | }, 665 | Rgb { 666 | r: 0xAF, 667 | g: 0x00, 668 | b: 0x00, 669 | }, 670 | Rgb { 671 | r: 0xAF, 672 | g: 0x00, 673 | b: 0x5F, 674 | }, 675 | Rgb { 676 | r: 0xAF, 677 | g: 0x00, 678 | b: 0x87, 679 | }, 680 | Rgb { 681 | r: 0xAF, 682 | g: 0x00, 683 | b: 0xAF, 684 | }, 685 | Rgb { 686 | r: 0xAF, 687 | g: 0x00, 688 | b: 0xD7, 689 | }, 690 | Rgb { 691 | r: 0xAF, 692 | g: 0x00, 693 | b: 0xFF, 694 | }, 695 | Rgb { 696 | r: 0xAF, 697 | g: 0x5F, 698 | b: 0x00, 699 | }, 700 | Rgb { 701 | r: 0xAF, 702 | g: 0x5F, 703 | b: 0x5F, 704 | }, 705 | Rgb { 706 | r: 0xAF, 707 | g: 0x5F, 708 | b: 0x87, 709 | }, 710 | Rgb { 711 | r: 0xAF, 712 | g: 0x5F, 713 | b: 0xAF, 714 | }, 715 | Rgb { 716 | r: 0xAF, 717 | g: 0x5F, 718 | b: 0xD7, 719 | }, 720 | Rgb { 721 | r: 0xAF, 722 | g: 0x5F, 723 | b: 0xFF, 724 | }, 725 | Rgb { 726 | r: 0xAF, 727 | g: 0x87, 728 | b: 0x00, 729 | }, 730 | Rgb { 731 | r: 0xAF, 732 | g: 0x87, 733 | b: 0x5F, 734 | }, 735 | Rgb { 736 | r: 0xAF, 737 | g: 0x87, 738 | b: 0x87, 739 | }, 740 | Rgb { 741 | r: 0xAF, 742 | g: 0x87, 743 | b: 0xAF, 744 | }, 745 | Rgb { 746 | r: 0xAF, 747 | g: 0x87, 748 | b: 0xD7, 749 | }, 750 | Rgb { 751 | r: 0xAF, 752 | g: 0x87, 753 | b: 0xFF, 754 | }, 755 | Rgb { 756 | r: 0xAF, 757 | g: 0xAF, 758 | b: 0x00, 759 | }, 760 | Rgb { 761 | r: 0xAF, 762 | g: 0xAF, 763 | b: 0x5F, 764 | }, 765 | Rgb { 766 | r: 0xAF, 767 | g: 0xAF, 768 | b: 0x87, 769 | }, 770 | Rgb { 771 | r: 0xAF, 772 | g: 0xAF, 773 | b: 0xAF, 774 | }, 775 | Rgb { 776 | r: 0xAF, 777 | g: 0xAF, 778 | b: 0xD7, 779 | }, 780 | Rgb { 781 | r: 0xAF, 782 | g: 0xAF, 783 | b: 0xFF, 784 | }, 785 | Rgb { 786 | r: 0xAF, 787 | g: 0xD7, 788 | b: 0x00, 789 | }, 790 | Rgb { 791 | r: 0xAF, 792 | g: 0xD7, 793 | b: 0x5F, 794 | }, 795 | Rgb { 796 | r: 0xAF, 797 | g: 0xD7, 798 | b: 0x87, 799 | }, 800 | Rgb { 801 | r: 0xAF, 802 | g: 0xD7, 803 | b: 0xAF, 804 | }, 805 | Rgb { 806 | r: 0xAF, 807 | g: 0xD7, 808 | b: 0xD7, 809 | }, 810 | Rgb { 811 | r: 0xAF, 812 | g: 0xD7, 813 | b: 0xFF, 814 | }, 815 | Rgb { 816 | r: 0xAF, 817 | g: 0xFF, 818 | b: 0x00, 819 | }, 820 | Rgb { 821 | r: 0xAF, 822 | g: 0xFF, 823 | b: 0x5F, 824 | }, 825 | Rgb { 826 | r: 0xAF, 827 | g: 0xFF, 828 | b: 0x87, 829 | }, 830 | Rgb { 831 | r: 0xAF, 832 | g: 0xFF, 833 | b: 0xAF, 834 | }, 835 | Rgb { 836 | r: 0xAF, 837 | g: 0xFF, 838 | b: 0xD7, 839 | }, 840 | Rgb { 841 | r: 0xAF, 842 | g: 0xFF, 843 | b: 0xFF, 844 | }, 845 | Rgb { 846 | r: 0xD7, 847 | g: 0x00, 848 | b: 0x00, 849 | }, 850 | Rgb { 851 | r: 0xD7, 852 | g: 0x00, 853 | b: 0x5F, 854 | }, 855 | Rgb { 856 | r: 0xD7, 857 | g: 0x00, 858 | b: 0x87, 859 | }, 860 | Rgb { 861 | r: 0xD7, 862 | g: 0x00, 863 | b: 0xAF, 864 | }, 865 | Rgb { 866 | r: 0xD7, 867 | g: 0x00, 868 | b: 0xD7, 869 | }, 870 | Rgb { 871 | r: 0xD7, 872 | g: 0x00, 873 | b: 0xFF, 874 | }, 875 | Rgb { 876 | r: 0xD7, 877 | g: 0x5F, 878 | b: 0x00, 879 | }, 880 | Rgb { 881 | r: 0xD7, 882 | g: 0x5F, 883 | b: 0x5F, 884 | }, 885 | Rgb { 886 | r: 0xD7, 887 | g: 0x5F, 888 | b: 0x87, 889 | }, 890 | Rgb { 891 | r: 0xD7, 892 | g: 0x5F, 893 | b: 0xAF, 894 | }, 895 | Rgb { 896 | r: 0xD7, 897 | g: 0x5F, 898 | b: 0xD7, 899 | }, 900 | Rgb { 901 | r: 0xD7, 902 | g: 0x5F, 903 | b: 0xFF, 904 | }, 905 | Rgb { 906 | r: 0xD7, 907 | g: 0x87, 908 | b: 0x00, 909 | }, 910 | Rgb { 911 | r: 0xD7, 912 | g: 0x87, 913 | b: 0x5F, 914 | }, 915 | Rgb { 916 | r: 0xD7, 917 | g: 0x87, 918 | b: 0x87, 919 | }, 920 | Rgb { 921 | r: 0xD7, 922 | g: 0x87, 923 | b: 0xAF, 924 | }, 925 | Rgb { 926 | r: 0xD7, 927 | g: 0x87, 928 | b: 0xD7, 929 | }, 930 | Rgb { 931 | r: 0xD7, 932 | g: 0x87, 933 | b: 0xFF, 934 | }, 935 | Rgb { 936 | r: 0xD7, 937 | g: 0xAF, 938 | b: 0x00, 939 | }, 940 | Rgb { 941 | r: 0xD7, 942 | g: 0xAF, 943 | b: 0x5F, 944 | }, 945 | Rgb { 946 | r: 0xD7, 947 | g: 0xAF, 948 | b: 0x87, 949 | }, 950 | Rgb { 951 | r: 0xD7, 952 | g: 0xAF, 953 | b: 0xAF, 954 | }, 955 | Rgb { 956 | r: 0xD7, 957 | g: 0xAF, 958 | b: 0xD7, 959 | }, 960 | Rgb { 961 | r: 0xD7, 962 | g: 0xAF, 963 | b: 0xFF, 964 | }, 965 | Rgb { 966 | r: 0xD7, 967 | g: 0xD7, 968 | b: 0x00, 969 | }, 970 | Rgb { 971 | r: 0xD7, 972 | g: 0xD7, 973 | b: 0x5F, 974 | }, 975 | Rgb { 976 | r: 0xD7, 977 | g: 0xD7, 978 | b: 0x87, 979 | }, 980 | Rgb { 981 | r: 0xD7, 982 | g: 0xD7, 983 | b: 0xAF, 984 | }, 985 | Rgb { 986 | r: 0xD7, 987 | g: 0xD7, 988 | b: 0xD7, 989 | }, 990 | Rgb { 991 | r: 0xD7, 992 | g: 0xD7, 993 | b: 0xFF, 994 | }, 995 | Rgb { 996 | r: 0xD7, 997 | g: 0xFF, 998 | b: 0x00, 999 | }, 1000 | Rgb { 1001 | r: 0xD7, 1002 | g: 0xFF, 1003 | b: 0x5F, 1004 | }, 1005 | Rgb { 1006 | r: 0xD7, 1007 | g: 0xFF, 1008 | b: 0x87, 1009 | }, 1010 | Rgb { 1011 | r: 0xD7, 1012 | g: 0xFF, 1013 | b: 0xAF, 1014 | }, 1015 | Rgb { 1016 | r: 0xD7, 1017 | g: 0xFF, 1018 | b: 0xD7, 1019 | }, 1020 | Rgb { 1021 | r: 0xD7, 1022 | g: 0xFF, 1023 | b: 0xFF, 1024 | }, 1025 | Rgb { 1026 | r: 0xFF, 1027 | g: 0x00, 1028 | b: 0x00, 1029 | }, 1030 | Rgb { 1031 | r: 0xFF, 1032 | g: 0x00, 1033 | b: 0x5F, 1034 | }, 1035 | Rgb { 1036 | r: 0xFF, 1037 | g: 0x00, 1038 | b: 0x87, 1039 | }, 1040 | Rgb { 1041 | r: 0xFF, 1042 | g: 0x00, 1043 | b: 0xAF, 1044 | }, 1045 | Rgb { 1046 | r: 0xFF, 1047 | g: 0x00, 1048 | b: 0xD7, 1049 | }, 1050 | Rgb { 1051 | r: 0xFF, 1052 | g: 0x00, 1053 | b: 0xFF, 1054 | }, 1055 | Rgb { 1056 | r: 0xFF, 1057 | g: 0x5F, 1058 | b: 0x00, 1059 | }, 1060 | Rgb { 1061 | r: 0xFF, 1062 | g: 0x5F, 1063 | b: 0x5F, 1064 | }, 1065 | Rgb { 1066 | r: 0xFF, 1067 | g: 0x5F, 1068 | b: 0x87, 1069 | }, 1070 | Rgb { 1071 | r: 0xFF, 1072 | g: 0x5F, 1073 | b: 0xAF, 1074 | }, 1075 | Rgb { 1076 | r: 0xFF, 1077 | g: 0x5F, 1078 | b: 0xD7, 1079 | }, 1080 | Rgb { 1081 | r: 0xFF, 1082 | g: 0x5F, 1083 | b: 0xFF, 1084 | }, 1085 | Rgb { 1086 | r: 0xFF, 1087 | g: 0x87, 1088 | b: 0x00, 1089 | }, 1090 | Rgb { 1091 | r: 0xFF, 1092 | g: 0x87, 1093 | b: 0x5F, 1094 | }, 1095 | Rgb { 1096 | r: 0xFF, 1097 | g: 0x87, 1098 | b: 0x87, 1099 | }, 1100 | Rgb { 1101 | r: 0xFF, 1102 | g: 0x87, 1103 | b: 0xAF, 1104 | }, 1105 | Rgb { 1106 | r: 0xFF, 1107 | g: 0x87, 1108 | b: 0xD7, 1109 | }, 1110 | Rgb { 1111 | r: 0xFF, 1112 | g: 0x87, 1113 | b: 0xFF, 1114 | }, 1115 | Rgb { 1116 | r: 0xFF, 1117 | g: 0xAF, 1118 | b: 0x00, 1119 | }, 1120 | Rgb { 1121 | r: 0xFF, 1122 | g: 0xAF, 1123 | b: 0x5F, 1124 | }, 1125 | Rgb { 1126 | r: 0xFF, 1127 | g: 0xAF, 1128 | b: 0x87, 1129 | }, 1130 | Rgb { 1131 | r: 0xFF, 1132 | g: 0xAF, 1133 | b: 0xAF, 1134 | }, 1135 | Rgb { 1136 | r: 0xFF, 1137 | g: 0xAF, 1138 | b: 0xD7, 1139 | }, 1140 | Rgb { 1141 | r: 0xFF, 1142 | g: 0xAF, 1143 | b: 0xFF, 1144 | }, 1145 | Rgb { 1146 | r: 0xFF, 1147 | g: 0xD7, 1148 | b: 0x00, 1149 | }, 1150 | Rgb { 1151 | r: 0xFF, 1152 | g: 0xD7, 1153 | b: 0x5F, 1154 | }, 1155 | Rgb { 1156 | r: 0xFF, 1157 | g: 0xD7, 1158 | b: 0x87, 1159 | }, 1160 | Rgb { 1161 | r: 0xFF, 1162 | g: 0xD7, 1163 | b: 0xAF, 1164 | }, 1165 | Rgb { 1166 | r: 0xFF, 1167 | g: 0xD7, 1168 | b: 0xD7, 1169 | }, 1170 | Rgb { 1171 | r: 0xFF, 1172 | g: 0xD7, 1173 | b: 0xFF, 1174 | }, 1175 | Rgb { 1176 | r: 0xFF, 1177 | g: 0xFF, 1178 | b: 0x00, 1179 | }, 1180 | Rgb { 1181 | r: 0xFF, 1182 | g: 0xFF, 1183 | b: 0x5F, 1184 | }, 1185 | Rgb { 1186 | r: 0xFF, 1187 | g: 0xFF, 1188 | b: 0x87, 1189 | }, 1190 | Rgb { 1191 | r: 0xFF, 1192 | g: 0xFF, 1193 | b: 0xAF, 1194 | }, 1195 | Rgb { 1196 | r: 0xFF, 1197 | g: 0xFF, 1198 | b: 0xD7, 1199 | }, 1200 | Rgb { 1201 | r: 0xFF, 1202 | g: 0xFF, 1203 | b: 0xFF, 1204 | }, 1205 | Rgb { 1206 | r: 0x08, 1207 | g: 0x08, 1208 | b: 0x08, 1209 | }, 1210 | Rgb { 1211 | r: 0x12, 1212 | g: 0x12, 1213 | b: 0x12, 1214 | }, 1215 | Rgb { 1216 | r: 0x1C, 1217 | g: 0x1C, 1218 | b: 0x1C, 1219 | }, 1220 | Rgb { 1221 | r: 0x26, 1222 | g: 0x26, 1223 | b: 0x26, 1224 | }, 1225 | Rgb { 1226 | r: 0x30, 1227 | g: 0x30, 1228 | b: 0x30, 1229 | }, 1230 | Rgb { 1231 | r: 0x3A, 1232 | g: 0x3A, 1233 | b: 0x3A, 1234 | }, 1235 | Rgb { 1236 | r: 0x44, 1237 | g: 0x44, 1238 | b: 0x44, 1239 | }, 1240 | Rgb { 1241 | r: 0x4E, 1242 | g: 0x4E, 1243 | b: 0x4E, 1244 | }, 1245 | Rgb { 1246 | r: 0x58, 1247 | g: 0x58, 1248 | b: 0x58, 1249 | }, 1250 | Rgb { 1251 | r: 0x60, 1252 | g: 0x60, 1253 | b: 0x60, 1254 | }, 1255 | Rgb { 1256 | r: 0x66, 1257 | g: 0x66, 1258 | b: 0x66, 1259 | }, 1260 | Rgb { 1261 | r: 0x76, 1262 | g: 0x76, 1263 | b: 0x76, 1264 | }, 1265 | Rgb { 1266 | r: 0x80, 1267 | g: 0x80, 1268 | b: 0x80, 1269 | }, 1270 | Rgb { 1271 | r: 0x8A, 1272 | g: 0x8A, 1273 | b: 0x8A, 1274 | }, 1275 | Rgb { 1276 | r: 0x94, 1277 | g: 0x94, 1278 | b: 0x94, 1279 | }, 1280 | Rgb { 1281 | r: 0x9E, 1282 | g: 0x9E, 1283 | b: 0x9E, 1284 | }, 1285 | Rgb { 1286 | r: 0xA8, 1287 | g: 0xA8, 1288 | b: 0xA8, 1289 | }, 1290 | Rgb { 1291 | r: 0xB2, 1292 | g: 0xB2, 1293 | b: 0xB2, 1294 | }, 1295 | Rgb { 1296 | r: 0xBC, 1297 | g: 0xBC, 1298 | b: 0xBC, 1299 | }, 1300 | Rgb { 1301 | r: 0xC6, 1302 | g: 0xC6, 1303 | b: 0xC6, 1304 | }, 1305 | Rgb { 1306 | r: 0xD0, 1307 | g: 0xD0, 1308 | b: 0xD0, 1309 | }, 1310 | Rgb { 1311 | r: 0xDA, 1312 | g: 0xDA, 1313 | b: 0xDA, 1314 | }, 1315 | Rgb { 1316 | r: 0xE4, 1317 | g: 0xE4, 1318 | b: 0xE4, 1319 | }, 1320 | Rgb { 1321 | r: 0xEE, 1322 | g: 0xEE, 1323 | b: 0xEE, 1324 | }, 1325 | ]; 1326 | 1327 | pub const ANSI_TO_HSL: &[Hsl] = &[ 1328 | Hsl { 1329 | h: 0.0, 1330 | s: 0.0, 1331 | l: 0.0, 1332 | }, 1333 | Hsl { 1334 | h: 0.0, 1335 | s: 1.0, 1336 | l: 0.25, 1337 | }, 1338 | Hsl { 1339 | h: 120.0, 1340 | s: 1.0, 1341 | l: 0.25, 1342 | }, 1343 | Hsl { 1344 | h: 60.0, 1345 | s: 1.0, 1346 | l: 0.25, 1347 | }, 1348 | Hsl { 1349 | h: 240.0, 1350 | s: 1.0, 1351 | l: 0.25, 1352 | }, 1353 | Hsl { 1354 | h: 300.0, 1355 | s: 1.0, 1356 | l: 0.25, 1357 | }, 1358 | Hsl { 1359 | h: 180.0, 1360 | s: 1.0, 1361 | l: 0.25, 1362 | }, 1363 | Hsl { 1364 | h: 0.0, 1365 | s: 0.0, 1366 | l: 0.75, 1367 | }, 1368 | Hsl { 1369 | h: 0.0, 1370 | s: 0.0, 1371 | l: 0.5, 1372 | }, 1373 | Hsl { 1374 | h: 0.0, 1375 | s: 1.0, 1376 | l: 0.49804688, 1377 | }, 1378 | Hsl { 1379 | h: 120.0, 1380 | s: 1.0, 1381 | l: 0.49804688, 1382 | }, 1383 | Hsl { 1384 | h: 60.0, 1385 | s: 1.0, 1386 | l: 0.49804688, 1387 | }, 1388 | Hsl { 1389 | h: 240.0, 1390 | s: 1.0, 1391 | l: 0.49804688, 1392 | }, 1393 | Hsl { 1394 | h: 300.0, 1395 | s: 1.0, 1396 | l: 0.49804688, 1397 | }, 1398 | Hsl { 1399 | h: 180.0, 1400 | s: 1.0, 1401 | l: 0.49804688, 1402 | }, 1403 | Hsl { 1404 | h: 0.0, 1405 | s: 0.0, 1406 | l: 0.99609375, 1407 | }, 1408 | Hsl { 1409 | h: 0.0, 1410 | s: 0.0, 1411 | l: 0.0, 1412 | }, 1413 | Hsl { 1414 | h: 240.0, 1415 | s: 1.0, 1416 | l: 0.18554688, 1417 | }, 1418 | Hsl { 1419 | h: 240.0, 1420 | s: 1.0, 1421 | l: 0.26367188, 1422 | }, 1423 | Hsl { 1424 | h: 240.0, 1425 | s: 1.0, 1426 | l: 0.34179688, 1427 | }, 1428 | Hsl { 1429 | h: 240.0, 1430 | s: 1.0, 1431 | l: 0.41992188, 1432 | }, 1433 | Hsl { 1434 | h: 240.0, 1435 | s: 1.0, 1436 | l: 0.49804688, 1437 | }, 1438 | Hsl { 1439 | h: 120.0, 1440 | s: 1.0, 1441 | l: 0.18554688, 1442 | }, 1443 | Hsl { 1444 | h: 180.0, 1445 | s: 1.0, 1446 | l: 0.18554688, 1447 | }, 1448 | Hsl { 1449 | h: 197.77777, 1450 | s: 1.0, 1451 | l: 0.26367188, 1452 | }, 1453 | Hsl { 1454 | h: 207.42859, 1455 | s: 1.0, 1456 | l: 0.34179688, 1457 | }, 1458 | Hsl { 1459 | h: 213.4884, 1460 | s: 1.0, 1461 | l: 0.41992188, 1462 | }, 1463 | Hsl { 1464 | h: 217.6471, 1465 | s: 1.0, 1466 | l: 0.49804688, 1467 | }, 1468 | Hsl { 1469 | h: 120.0, 1470 | s: 1.0, 1471 | l: 0.26367188, 1472 | }, 1473 | Hsl { 1474 | h: 162.22223, 1475 | s: 1.0, 1476 | l: 0.26367188, 1477 | }, 1478 | Hsl { 1479 | h: 180.0, 1480 | s: 1.0, 1481 | l: 0.26367188, 1482 | }, 1483 | Hsl { 1484 | h: 193.7143, 1485 | s: 1.0, 1486 | l: 0.34179688, 1487 | }, 1488 | Hsl { 1489 | h: 202.32556, 1490 | s: 1.0, 1491 | l: 0.41992188, 1492 | }, 1493 | Hsl { 1494 | h: 208.23529, 1495 | s: 1.0, 1496 | l: 0.49804688, 1497 | }, 1498 | Hsl { 1499 | h: 120.0, 1500 | s: 1.0, 1501 | l: 0.34179688, 1502 | }, 1503 | Hsl { 1504 | h: 152.57141, 1505 | s: 1.0, 1506 | l: 0.34179688, 1507 | }, 1508 | Hsl { 1509 | h: 166.2857, 1510 | s: 1.0, 1511 | l: 0.34179688, 1512 | }, 1513 | Hsl { 1514 | h: 180.0, 1515 | s: 1.0, 1516 | l: 0.34179688, 1517 | }, 1518 | Hsl { 1519 | h: 191.16278, 1520 | s: 1.0, 1521 | l: 0.41992188, 1522 | }, 1523 | Hsl { 1524 | h: 198.82355, 1525 | s: 1.0, 1526 | l: 0.49804688, 1527 | }, 1528 | Hsl { 1529 | h: 120.0, 1530 | s: 1.0, 1531 | l: 0.41992188, 1532 | }, 1533 | Hsl { 1534 | h: 146.51163, 1535 | s: 1.0, 1536 | l: 0.41992188, 1537 | }, 1538 | Hsl { 1539 | h: 157.67444, 1540 | s: 1.0, 1541 | l: 0.41992188, 1542 | }, 1543 | Hsl { 1544 | h: 168.83722, 1545 | s: 1.0, 1546 | l: 0.41992188, 1547 | }, 1548 | Hsl { 1549 | h: 180.0, 1550 | s: 1.0, 1551 | l: 0.41992188, 1552 | }, 1553 | Hsl { 1554 | h: 189.41174, 1555 | s: 1.0, 1556 | l: 0.49804688, 1557 | }, 1558 | Hsl { 1559 | h: 120.0, 1560 | s: 1.0, 1561 | l: 0.49804688, 1562 | }, 1563 | Hsl { 1564 | h: 142.35294, 1565 | s: 1.0, 1566 | l: 0.49804688, 1567 | }, 1568 | Hsl { 1569 | h: 151.76471, 1570 | s: 1.0, 1571 | l: 0.49804688, 1572 | }, 1573 | Hsl { 1574 | h: 161.17645, 1575 | s: 1.0, 1576 | l: 0.49804688, 1577 | }, 1578 | Hsl { 1579 | h: 170.58826, 1580 | s: 1.0, 1581 | l: 0.49804688, 1582 | }, 1583 | Hsl { 1584 | h: 180.0, 1585 | s: 1.0, 1586 | l: 0.49804688, 1587 | }, 1588 | Hsl { 1589 | h: 0.0, 1590 | s: 1.0, 1591 | l: 0.18554688, 1592 | }, 1593 | Hsl { 1594 | h: 300.0, 1595 | s: 1.0, 1596 | l: 0.18554688, 1597 | }, 1598 | Hsl { 1599 | h: 282.22223, 1600 | s: 1.0, 1601 | l: 0.26367188, 1602 | }, 1603 | Hsl { 1604 | h: 272.5714, 1605 | s: 1.0, 1606 | l: 0.34179688, 1607 | }, 1608 | Hsl { 1609 | h: 266.5116, 1610 | s: 1.0, 1611 | l: 0.41992188, 1612 | }, 1613 | Hsl { 1614 | h: 262.3529, 1615 | s: 1.0, 1616 | l: 0.49804688, 1617 | }, 1618 | Hsl { 1619 | h: 60.0, 1620 | s: 1.0, 1621 | l: 0.18554688, 1622 | }, 1623 | Hsl { 1624 | h: 0.0, 1625 | s: 0.0, 1626 | l: 0.37109375, 1627 | }, 1628 | Hsl { 1629 | h: 240.0, 1630 | s: 0.17391305, 1631 | l: 0.44921875, 1632 | }, 1633 | Hsl { 1634 | h: 240.0, 1635 | s: 0.3305785, 1636 | l: 0.52734375, 1637 | }, 1638 | Hsl { 1639 | h: 240.0, 1640 | s: 0.5940594, 1641 | l: 0.60546875, 1642 | }, 1643 | Hsl { 1644 | h: 240.0, 1645 | s: 0.9876543, 1646 | l: 0.68359375, 1647 | }, 1648 | Hsl { 1649 | h: 77.77777, 1650 | s: 1.0, 1651 | l: 0.26367188, 1652 | }, 1653 | Hsl { 1654 | h: 120.0, 1655 | s: 0.17391305, 1656 | l: 0.44921875, 1657 | }, 1658 | Hsl { 1659 | h: 180.0, 1660 | s: 0.17391305, 1661 | l: 0.44921875, 1662 | }, 1663 | Hsl { 1664 | h: 210.0, 1665 | s: 0.3305785, 1666 | l: 0.52734375, 1667 | }, 1668 | Hsl { 1669 | h: 220.0, 1670 | s: 0.5940594, 1671 | l: 0.60546875, 1672 | }, 1673 | Hsl { 1674 | h: 225.0, 1675 | s: 0.9876543, 1676 | l: 0.68359375, 1677 | }, 1678 | Hsl { 1679 | h: 87.42859, 1680 | s: 1.0, 1681 | l: 0.34179688, 1682 | }, 1683 | Hsl { 1684 | h: 120.0, 1685 | s: 0.3305785, 1686 | l: 0.52734375, 1687 | }, 1688 | Hsl { 1689 | h: 150.0, 1690 | s: 0.3305785, 1691 | l: 0.52734375, 1692 | }, 1693 | Hsl { 1694 | h: 180.0, 1695 | s: 0.3305785, 1696 | l: 0.52734375, 1697 | }, 1698 | Hsl { 1699 | h: 200.0, 1700 | s: 0.5940594, 1701 | l: 0.60546875, 1702 | }, 1703 | Hsl { 1704 | h: 210.0, 1705 | s: 0.9876543, 1706 | l: 0.68359375, 1707 | }, 1708 | Hsl { 1709 | h: 93.48837, 1710 | s: 1.0, 1711 | l: 0.41992188, 1712 | }, 1713 | Hsl { 1714 | h: 120.0, 1715 | s: 0.5940594, 1716 | l: 0.60546875, 1717 | }, 1718 | Hsl { 1719 | h: 140.0, 1720 | s: 0.5940594, 1721 | l: 0.60546875, 1722 | }, 1723 | Hsl { 1724 | h: 160.0, 1725 | s: 0.5940594, 1726 | l: 0.60546875, 1727 | }, 1728 | Hsl { 1729 | h: 180.0, 1730 | s: 0.5940594, 1731 | l: 0.60546875, 1732 | }, 1733 | Hsl { 1734 | h: 195.0, 1735 | s: 0.9876543, 1736 | l: 0.68359375, 1737 | }, 1738 | Hsl { 1739 | h: 97.647064, 1740 | s: 1.0, 1741 | l: 0.49804688, 1742 | }, 1743 | Hsl { 1744 | h: 120.0, 1745 | s: 0.9876543, 1746 | l: 0.68359375, 1747 | }, 1748 | Hsl { 1749 | h: 135.0, 1750 | s: 0.9876543, 1751 | l: 0.68359375, 1752 | }, 1753 | Hsl { 1754 | h: 150.0, 1755 | s: 0.9876543, 1756 | l: 0.68359375, 1757 | }, 1758 | Hsl { 1759 | h: 165.0, 1760 | s: 0.9876543, 1761 | l: 0.68359375, 1762 | }, 1763 | Hsl { 1764 | h: 180.0, 1765 | s: 0.9876543, 1766 | l: 0.68359375, 1767 | }, 1768 | Hsl { 1769 | h: 0.0, 1770 | s: 1.0, 1771 | l: 0.26367188, 1772 | }, 1773 | Hsl { 1774 | h: 317.77777, 1775 | s: 1.0, 1776 | l: 0.26367188, 1777 | }, 1778 | Hsl { 1779 | h: 300.0, 1780 | s: 1.0, 1781 | l: 0.26367188, 1782 | }, 1783 | Hsl { 1784 | h: 286.2857, 1785 | s: 1.0, 1786 | l: 0.34179688, 1787 | }, 1788 | Hsl { 1789 | h: 277.67444, 1790 | s: 1.0, 1791 | l: 0.41992188, 1792 | }, 1793 | Hsl { 1794 | h: 271.7647, 1795 | s: 1.0, 1796 | l: 0.49804688, 1797 | }, 1798 | Hsl { 1799 | h: 42.22223, 1800 | s: 1.0, 1801 | l: 0.26367188, 1802 | }, 1803 | Hsl { 1804 | h: 0.0, 1805 | s: 0.17391305, 1806 | l: 0.44921875, 1807 | }, 1808 | Hsl { 1809 | h: 300.0, 1810 | s: 0.17391305, 1811 | l: 0.44921875, 1812 | }, 1813 | Hsl { 1814 | h: 270.0, 1815 | s: 0.3305785, 1816 | l: 0.52734375, 1817 | }, 1818 | Hsl { 1819 | h: 260.0, 1820 | s: 0.5940594, 1821 | l: 0.60546875, 1822 | }, 1823 | Hsl { 1824 | h: 255.0, 1825 | s: 0.9876543, 1826 | l: 0.68359375, 1827 | }, 1828 | Hsl { 1829 | h: 60.0, 1830 | s: 1.0, 1831 | l: 0.26367188, 1832 | }, 1833 | Hsl { 1834 | h: 60.0, 1835 | s: 0.17391305, 1836 | l: 0.44921875, 1837 | }, 1838 | Hsl { 1839 | h: 0.0, 1840 | s: 0.0, 1841 | l: 0.52734375, 1842 | }, 1843 | Hsl { 1844 | h: 240.0, 1845 | s: 0.1980198, 1846 | l: 0.60546875, 1847 | }, 1848 | Hsl { 1849 | h: 240.0, 1850 | s: 0.49382716, 1851 | l: 0.68359375, 1852 | }, 1853 | Hsl { 1854 | h: 240.0, 1855 | s: 0.9836066, 1856 | l: 0.76171875, 1857 | }, 1858 | Hsl { 1859 | h: 73.714294, 1860 | s: 1.0, 1861 | l: 0.34179688, 1862 | }, 1863 | Hsl { 1864 | h: 90.0, 1865 | s: 0.3305785, 1866 | l: 0.52734375, 1867 | }, 1868 | Hsl { 1869 | h: 120.0, 1870 | s: 0.1980198, 1871 | l: 0.60546875, 1872 | }, 1873 | Hsl { 1874 | h: 180.0, 1875 | s: 0.1980198, 1876 | l: 0.60546875, 1877 | }, 1878 | Hsl { 1879 | h: 210.0, 1880 | s: 0.49382716, 1881 | l: 0.68359375, 1882 | }, 1883 | Hsl { 1884 | h: 220.0, 1885 | s: 0.9836066, 1886 | l: 0.76171875, 1887 | }, 1888 | Hsl { 1889 | h: 82.32556, 1890 | s: 1.0, 1891 | l: 0.41992188, 1892 | }, 1893 | Hsl { 1894 | h: 100.0, 1895 | s: 0.5940594, 1896 | l: 0.60546875, 1897 | }, 1898 | Hsl { 1899 | h: 120.0, 1900 | s: 0.49382716, 1901 | l: 0.68359375, 1902 | }, 1903 | Hsl { 1904 | h: 150.0, 1905 | s: 0.49382716, 1906 | l: 0.68359375, 1907 | }, 1908 | Hsl { 1909 | h: 180.0, 1910 | s: 0.49382716, 1911 | l: 0.68359375, 1912 | }, 1913 | Hsl { 1914 | h: 200.0, 1915 | s: 0.9836066, 1916 | l: 0.76171875, 1917 | }, 1918 | Hsl { 1919 | h: 88.23529, 1920 | s: 1.0, 1921 | l: 0.49804688, 1922 | }, 1923 | Hsl { 1924 | h: 105.0, 1925 | s: 0.9876543, 1926 | l: 0.68359375, 1927 | }, 1928 | Hsl { 1929 | h: 120.0, 1930 | s: 0.9836066, 1931 | l: 0.76171875, 1932 | }, 1933 | Hsl { 1934 | h: 140.0, 1935 | s: 0.9836066, 1936 | l: 0.76171875, 1937 | }, 1938 | Hsl { 1939 | h: 160.0, 1940 | s: 0.9836066, 1941 | l: 0.76171875, 1942 | }, 1943 | Hsl { 1944 | h: 180.0, 1945 | s: 0.9836066, 1946 | l: 0.76171875, 1947 | }, 1948 | Hsl { 1949 | h: 0.0, 1950 | s: 1.0, 1951 | l: 0.34179688, 1952 | }, 1953 | Hsl { 1954 | h: 327.42856, 1955 | s: 1.0, 1956 | l: 0.34179688, 1957 | }, 1958 | Hsl { 1959 | h: 313.7143, 1960 | s: 1.0, 1961 | l: 0.34179688, 1962 | }, 1963 | Hsl { 1964 | h: 300.0, 1965 | s: 1.0, 1966 | l: 0.34179688, 1967 | }, 1968 | Hsl { 1969 | h: 288.83722, 1970 | s: 1.0, 1971 | l: 0.41992188, 1972 | }, 1973 | Hsl { 1974 | h: 281.1765, 1975 | s: 1.0, 1976 | l: 0.49804688, 1977 | }, 1978 | Hsl { 1979 | h: 32.57144, 1980 | s: 1.0, 1981 | l: 0.34179688, 1982 | }, 1983 | Hsl { 1984 | h: 0.0, 1985 | s: 0.3305785, 1986 | l: 0.52734375, 1987 | }, 1988 | Hsl { 1989 | h: 330.0, 1990 | s: 0.3305785, 1991 | l: 0.52734375, 1992 | }, 1993 | Hsl { 1994 | h: 300.0, 1995 | s: 0.3305785, 1996 | l: 0.52734375, 1997 | }, 1998 | Hsl { 1999 | h: 280.0, 2000 | s: 0.5940594, 2001 | l: 0.60546875, 2002 | }, 2003 | Hsl { 2004 | h: 270.0, 2005 | s: 0.9876543, 2006 | l: 0.68359375, 2007 | }, 2008 | Hsl { 2009 | h: 46.285706, 2010 | s: 1.0, 2011 | l: 0.34179688, 2012 | }, 2013 | Hsl { 2014 | h: 30.0, 2015 | s: 0.3305785, 2016 | l: 0.52734375, 2017 | }, 2018 | Hsl { 2019 | h: 0.0, 2020 | s: 0.1980198, 2021 | l: 0.60546875, 2022 | }, 2023 | Hsl { 2024 | h: 300.0, 2025 | s: 0.1980198, 2026 | l: 0.60546875, 2027 | }, 2028 | Hsl { 2029 | h: 270.0, 2030 | s: 0.49382716, 2031 | l: 0.68359375, 2032 | }, 2033 | Hsl { 2034 | h: 260.0, 2035 | s: 0.9836066, 2036 | l: 0.76171875, 2037 | }, 2038 | Hsl { 2039 | h: 60.0, 2040 | s: 1.0, 2041 | l: 0.34179688, 2042 | }, 2043 | Hsl { 2044 | h: 60.0, 2045 | s: 0.3305785, 2046 | l: 0.52734375, 2047 | }, 2048 | Hsl { 2049 | h: 60.0, 2050 | s: 0.1980198, 2051 | l: 0.60546875, 2052 | }, 2053 | Hsl { 2054 | h: 0.0, 2055 | s: 0.0, 2056 | l: 0.68359375, 2057 | }, 2058 | Hsl { 2059 | h: 240.0, 2060 | s: 0.32786885, 2061 | l: 0.76171875, 2062 | }, 2063 | Hsl { 2064 | h: 240.0, 2065 | s: 0.9756098, 2066 | l: 0.83984375, 2067 | }, 2068 | Hsl { 2069 | h: 71.16278, 2070 | s: 1.0, 2071 | l: 0.41992188, 2072 | }, 2073 | Hsl { 2074 | h: 80.0, 2075 | s: 0.5940594, 2076 | l: 0.60546875, 2077 | }, 2078 | Hsl { 2079 | h: 90.0, 2080 | s: 0.49382716, 2081 | l: 0.68359375, 2082 | }, 2083 | Hsl { 2084 | h: 120.0, 2085 | s: 0.32786885, 2086 | l: 0.76171875, 2087 | }, 2088 | Hsl { 2089 | h: 180.0, 2090 | s: 0.32786885, 2091 | l: 0.76171875, 2092 | }, 2093 | Hsl { 2094 | h: 210.0, 2095 | s: 0.9756098, 2096 | l: 0.83984375, 2097 | }, 2098 | Hsl { 2099 | h: 78.82355, 2100 | s: 1.0, 2101 | l: 0.49804688, 2102 | }, 2103 | Hsl { 2104 | h: 90.0, 2105 | s: 0.9876543, 2106 | l: 0.68359375, 2107 | }, 2108 | Hsl { 2109 | h: 100.0, 2110 | s: 0.9836066, 2111 | l: 0.76171875, 2112 | }, 2113 | Hsl { 2114 | h: 120.0, 2115 | s: 0.9756098, 2116 | l: 0.83984375, 2117 | }, 2118 | Hsl { 2119 | h: 150.0, 2120 | s: 0.9756098, 2121 | l: 0.83984375, 2122 | }, 2123 | Hsl { 2124 | h: 180.0, 2125 | s: 0.9756098, 2126 | l: 0.83984375, 2127 | }, 2128 | Hsl { 2129 | h: 0.0, 2130 | s: 1.0, 2131 | l: 0.41992188, 2132 | }, 2133 | Hsl { 2134 | h: 333.48837, 2135 | s: 1.0, 2136 | l: 0.41992188, 2137 | }, 2138 | Hsl { 2139 | h: 322.3256, 2140 | s: 1.0, 2141 | l: 0.41992188, 2142 | }, 2143 | Hsl { 2144 | h: 311.16278, 2145 | s: 1.0, 2146 | l: 0.41992188, 2147 | }, 2148 | Hsl { 2149 | h: 300.0, 2150 | s: 1.0, 2151 | l: 0.41992188, 2152 | }, 2153 | Hsl { 2154 | h: 290.58826, 2155 | s: 1.0, 2156 | l: 0.49804688, 2157 | }, 2158 | Hsl { 2159 | h: 26.511627, 2160 | s: 1.0, 2161 | l: 0.41992188, 2162 | }, 2163 | Hsl { 2164 | h: 0.0, 2165 | s: 0.5940594, 2166 | l: 0.60546875, 2167 | }, 2168 | Hsl { 2169 | h: 340.0, 2170 | s: 0.5940594, 2171 | l: 0.60546875, 2172 | }, 2173 | Hsl { 2174 | h: 320.0, 2175 | s: 0.5940594, 2176 | l: 0.60546875, 2177 | }, 2178 | Hsl { 2179 | h: 300.0, 2180 | s: 0.5940594, 2181 | l: 0.60546875, 2182 | }, 2183 | Hsl { 2184 | h: 285.0, 2185 | s: 0.9876543, 2186 | l: 0.68359375, 2187 | }, 2188 | Hsl { 2189 | h: 37.674408, 2190 | s: 1.0, 2191 | l: 0.41992188, 2192 | }, 2193 | Hsl { 2194 | h: 20.0, 2195 | s: 0.5940594, 2196 | l: 0.60546875, 2197 | }, 2198 | Hsl { 2199 | h: 0.0, 2200 | s: 0.49382716, 2201 | l: 0.68359375, 2202 | }, 2203 | Hsl { 2204 | h: 330.0, 2205 | s: 0.49382716, 2206 | l: 0.68359375, 2207 | }, 2208 | Hsl { 2209 | h: 300.0, 2210 | s: 0.49382716, 2211 | l: 0.68359375, 2212 | }, 2213 | Hsl { 2214 | h: 280.0, 2215 | s: 0.9836066, 2216 | l: 0.76171875, 2217 | }, 2218 | Hsl { 2219 | h: 48.83722, 2220 | s: 1.0, 2221 | l: 0.41992188, 2222 | }, 2223 | Hsl { 2224 | h: 40.0, 2225 | s: 0.5940594, 2226 | l: 0.60546875, 2227 | }, 2228 | Hsl { 2229 | h: 30.0, 2230 | s: 0.49382716, 2231 | l: 0.68359375, 2232 | }, 2233 | Hsl { 2234 | h: 0.0, 2235 | s: 0.32786885, 2236 | l: 0.76171875, 2237 | }, 2238 | Hsl { 2239 | h: 300.0, 2240 | s: 0.32786885, 2241 | l: 0.76171875, 2242 | }, 2243 | Hsl { 2244 | h: 270.0, 2245 | s: 0.9756098, 2246 | l: 0.83984375, 2247 | }, 2248 | Hsl { 2249 | h: 60.0, 2250 | s: 1.0, 2251 | l: 0.41992188, 2252 | }, 2253 | Hsl { 2254 | h: 60.0, 2255 | s: 0.5940594, 2256 | l: 0.60546875, 2257 | }, 2258 | Hsl { 2259 | h: 60.0, 2260 | s: 0.49382716, 2261 | l: 0.68359375, 2262 | }, 2263 | Hsl { 2264 | h: 60.0, 2265 | s: 0.32786885, 2266 | l: 0.76171875, 2267 | }, 2268 | Hsl { 2269 | h: 0.0, 2270 | s: 0.0, 2271 | l: 0.83984375, 2272 | }, 2273 | Hsl { 2274 | h: 240.0, 2275 | s: 0.95238096, 2276 | l: 0.91796875, 2277 | }, 2278 | Hsl { 2279 | h: 69.41177, 2280 | s: 1.0, 2281 | l: 0.49804688, 2282 | }, 2283 | Hsl { 2284 | h: 75.0, 2285 | s: 0.9876543, 2286 | l: 0.68359375, 2287 | }, 2288 | Hsl { 2289 | h: 80.0, 2290 | s: 0.9836066, 2291 | l: 0.76171875, 2292 | }, 2293 | Hsl { 2294 | h: 90.0, 2295 | s: 0.9756098, 2296 | l: 0.83984375, 2297 | }, 2298 | Hsl { 2299 | h: 120.0, 2300 | s: 0.95238096, 2301 | l: 0.91796875, 2302 | }, 2303 | Hsl { 2304 | h: 180.0, 2305 | s: 0.95238096, 2306 | l: 0.91796875, 2307 | }, 2308 | Hsl { 2309 | h: 0.0, 2310 | s: 1.0, 2311 | l: 0.49804688, 2312 | }, 2313 | Hsl { 2314 | h: 337.64706, 2315 | s: 1.0, 2316 | l: 0.49804688, 2317 | }, 2318 | Hsl { 2319 | h: 328.2353, 2320 | s: 1.0, 2321 | l: 0.49804688, 2322 | }, 2323 | Hsl { 2324 | h: 318.82352, 2325 | s: 1.0, 2326 | l: 0.49804688, 2327 | }, 2328 | Hsl { 2329 | h: 309.41177, 2330 | s: 1.0, 2331 | l: 0.49804688, 2332 | }, 2333 | Hsl { 2334 | h: 300.0, 2335 | s: 1.0, 2336 | l: 0.49804688, 2337 | }, 2338 | Hsl { 2339 | h: 22.352936, 2340 | s: 1.0, 2341 | l: 0.49804688, 2342 | }, 2343 | Hsl { 2344 | h: 0.0, 2345 | s: 0.9876543, 2346 | l: 0.68359375, 2347 | }, 2348 | Hsl { 2349 | h: 345.0, 2350 | s: 0.9876543, 2351 | l: 0.68359375, 2352 | }, 2353 | Hsl { 2354 | h: 330.0, 2355 | s: 0.9876543, 2356 | l: 0.68359375, 2357 | }, 2358 | Hsl { 2359 | h: 315.0, 2360 | s: 0.9876543, 2361 | l: 0.68359375, 2362 | }, 2363 | Hsl { 2364 | h: 300.0, 2365 | s: 0.9876543, 2366 | l: 0.68359375, 2367 | }, 2368 | Hsl { 2369 | h: 31.76471, 2370 | s: 1.0, 2371 | l: 0.49804688, 2372 | }, 2373 | Hsl { 2374 | h: 15.0, 2375 | s: 0.9876543, 2376 | l: 0.68359375, 2377 | }, 2378 | Hsl { 2379 | h: 0.0, 2380 | s: 0.9836066, 2381 | l: 0.76171875, 2382 | }, 2383 | Hsl { 2384 | h: 340.0, 2385 | s: 0.9836066, 2386 | l: 0.76171875, 2387 | }, 2388 | Hsl { 2389 | h: 320.0, 2390 | s: 0.9836066, 2391 | l: 0.76171875, 2392 | }, 2393 | Hsl { 2394 | h: 300.0, 2395 | s: 0.9836066, 2396 | l: 0.76171875, 2397 | }, 2398 | Hsl { 2399 | h: 41.176483, 2400 | s: 1.0, 2401 | l: 0.49804688, 2402 | }, 2403 | Hsl { 2404 | h: 30.0, 2405 | s: 0.9876543, 2406 | l: 0.68359375, 2407 | }, 2408 | Hsl { 2409 | h: 20.0, 2410 | s: 0.9836066, 2411 | l: 0.76171875, 2412 | }, 2413 | Hsl { 2414 | h: 0.0, 2415 | s: 0.9756098, 2416 | l: 0.83984375, 2417 | }, 2418 | Hsl { 2419 | h: 330.0, 2420 | s: 0.9756098, 2421 | l: 0.83984375, 2422 | }, 2423 | Hsl { 2424 | h: 300.0, 2425 | s: 0.9756098, 2426 | l: 0.83984375, 2427 | }, 2428 | Hsl { 2429 | h: 50.588226, 2430 | s: 1.0, 2431 | l: 0.49804688, 2432 | }, 2433 | Hsl { 2434 | h: 45.0, 2435 | s: 0.9876543, 2436 | l: 0.68359375, 2437 | }, 2438 | Hsl { 2439 | h: 40.0, 2440 | s: 0.9836066, 2441 | l: 0.76171875, 2442 | }, 2443 | Hsl { 2444 | h: 30.0, 2445 | s: 0.9756098, 2446 | l: 0.83984375, 2447 | }, 2448 | Hsl { 2449 | h: 0.0, 2450 | s: 0.95238096, 2451 | l: 0.91796875, 2452 | }, 2453 | Hsl { 2454 | h: 300.0, 2455 | s: 0.95238096, 2456 | l: 0.91796875, 2457 | }, 2458 | Hsl { 2459 | h: 60.0, 2460 | s: 1.0, 2461 | l: 0.49804688, 2462 | }, 2463 | Hsl { 2464 | h: 60.0, 2465 | s: 0.9876543, 2466 | l: 0.68359375, 2467 | }, 2468 | Hsl { 2469 | h: 60.0, 2470 | s: 0.9836066, 2471 | l: 0.76171875, 2472 | }, 2473 | Hsl { 2474 | h: 60.0, 2475 | s: 0.9756098, 2476 | l: 0.83984375, 2477 | }, 2478 | Hsl { 2479 | h: 60.0, 2480 | s: 0.95238096, 2481 | l: 0.91796875, 2482 | }, 2483 | Hsl { 2484 | h: 0.0, 2485 | s: 0.0, 2486 | l: 0.99609375, 2487 | }, 2488 | Hsl { 2489 | h: 0.0, 2490 | s: 0.0, 2491 | l: 0.03125, 2492 | }, 2493 | Hsl { 2494 | h: 0.0, 2495 | s: 0.0, 2496 | l: 0.0703125, 2497 | }, 2498 | Hsl { 2499 | h: 0.0, 2500 | s: 0.0, 2501 | l: 0.109375, 2502 | }, 2503 | Hsl { 2504 | h: 0.0, 2505 | s: 0.0, 2506 | l: 0.1484375, 2507 | }, 2508 | Hsl { 2509 | h: 0.0, 2510 | s: 0.0, 2511 | l: 0.1875, 2512 | }, 2513 | Hsl { 2514 | h: 0.0, 2515 | s: 0.0, 2516 | l: 0.2265625, 2517 | }, 2518 | Hsl { 2519 | h: 0.0, 2520 | s: 0.0, 2521 | l: 0.265625, 2522 | }, 2523 | Hsl { 2524 | h: 0.0, 2525 | s: 0.0, 2526 | l: 0.3046875, 2527 | }, 2528 | Hsl { 2529 | h: 0.0, 2530 | s: 0.0, 2531 | l: 0.34375, 2532 | }, 2533 | Hsl { 2534 | h: 0.0, 2535 | s: 0.0, 2536 | l: 0.375, 2537 | }, 2538 | Hsl { 2539 | h: 0.0, 2540 | s: 0.0, 2541 | l: 0.3984375, 2542 | }, 2543 | Hsl { 2544 | h: 0.0, 2545 | s: 0.0, 2546 | l: 0.4609375, 2547 | }, 2548 | Hsl { 2549 | h: 0.0, 2550 | s: 0.0, 2551 | l: 0.5, 2552 | }, 2553 | Hsl { 2554 | h: 0.0, 2555 | s: 0.0, 2556 | l: 0.5390625, 2557 | }, 2558 | Hsl { 2559 | h: 0.0, 2560 | s: 0.0, 2561 | l: 0.578125, 2562 | }, 2563 | Hsl { 2564 | h: 0.0, 2565 | s: 0.0, 2566 | l: 0.6171875, 2567 | }, 2568 | Hsl { 2569 | h: 0.0, 2570 | s: 0.0, 2571 | l: 0.65625, 2572 | }, 2573 | Hsl { 2574 | h: 0.0, 2575 | s: 0.0, 2576 | l: 0.6953125, 2577 | }, 2578 | Hsl { 2579 | h: 0.0, 2580 | s: 0.0, 2581 | l: 0.734375, 2582 | }, 2583 | Hsl { 2584 | h: 0.0, 2585 | s: 0.0, 2586 | l: 0.7734375, 2587 | }, 2588 | Hsl { 2589 | h: 0.0, 2590 | s: 0.0, 2591 | l: 0.8125, 2592 | }, 2593 | Hsl { 2594 | h: 0.0, 2595 | s: 0.0, 2596 | l: 0.8515625, 2597 | }, 2598 | Hsl { 2599 | h: 0.0, 2600 | s: 0.0, 2601 | l: 0.890625, 2602 | }, 2603 | Hsl { 2604 | h: 0.0, 2605 | s: 0.0, 2606 | l: 0.9296875, 2607 | }, 2608 | ]; 2609 | 2610 | #[test] 2611 | fn check_array_lengths() { 2612 | // just check I properly generated the tables ^^ 2613 | assert_eq!(ANSI_TO_RGB.len(), 256); 2614 | assert_eq!(ANSI_TO_HSL.len(), 256); 2615 | } 2616 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[cfg(feature = "crossterm")] 4 | use crossterm::style::Color as CC; 5 | 6 | /// Color type, may be Ansi, Hsl or Rgb 7 | #[derive(Clone, Copy, Debug)] 8 | pub enum Color { 9 | Ansi(AnsiColor), 10 | Hsl(Hsl), 11 | Rgb(Rgb), 12 | } 13 | 14 | impl Color { 15 | pub fn ansi(self) -> AnsiColor { 16 | match self { 17 | Self::Ansi(ansi) => ansi, 18 | Self::Hsl(hsl) => hsl.to_ansi(), 19 | Self::Rgb(rgb) => rgb.to_ansi(), 20 | } 21 | } 22 | pub fn hsl(self) -> Hsl { 23 | match self { 24 | Self::Ansi(ansi) => ansi.to_hsl(), 25 | Self::Hsl(hsl) => hsl, 26 | Self::Rgb(rgb) => rgb.to_hsl(), 27 | } 28 | } 29 | pub fn rgb(self) -> Rgb { 30 | match self { 31 | Self::Ansi(ansi) => ansi.to_rgb(), 32 | Self::Hsl(hsl) => hsl.to_rgb(), 33 | Self::Rgb(rgb) => rgb, 34 | } 35 | } 36 | pub fn luma(self) -> f32 { 37 | self.rgb().luma() 38 | } 39 | /// compute a natural feeling intermediate between two colors 40 | pub fn blend, C2: Into>(c1: C1, w1: f32, c2: C2, w2: f32) -> Self { 41 | let c1: Color = c1.into(); 42 | let c2: Color = c2.into(); 43 | debug_assert!(w1 + w2 > 0.0); 44 | let hsl1: Hsl = c1.hsl(); 45 | let hsl2: Hsl = c2.hsl(); 46 | let mixed_hsl = Hsl::mix(hsl1, w1, hsl2, w2); 47 | let rgb1: Rgb = hsl1.to_rgb(); 48 | let rgb2: Rgb = hsl2.to_rgb(); 49 | let mixed_rgb = Rgb::mix(rgb1, w1, rgb2, w2); 50 | let mixed_rgb_hsl = mixed_rgb.to_hsl(); 51 | let mixed = Hsl::mix(mixed_hsl, 0.5, mixed_rgb_hsl, 0.5); 52 | Hsl { 53 | h: mixed_rgb_hsl.h, // hue blending done only on rgb space 54 | s: mixed.s, // saturation is mix between RGB computation and HSL one 55 | l: mixed.l, // luminosity is mix between RGB computation and HSL one 56 | } 57 | .into() 58 | } 59 | } 60 | 61 | impl From for Color { 62 | fn from(ansi: AnsiColor) -> Self { 63 | Self::Ansi(ansi) 64 | } 65 | } 66 | impl From for Color { 67 | fn from(rgb: Rgb) -> Self { 68 | Self::Rgb(rgb) 69 | } 70 | } 71 | impl From for Color { 72 | fn from(rgb: Hsl) -> Self { 73 | Self::Hsl(rgb) 74 | } 75 | } 76 | impl From for Color { 77 | fn from(code: u8) -> Self { 78 | Self::Ansi(AnsiColor::new(code)) 79 | } 80 | } 81 | 82 | #[cfg(feature = "crossterm")] 83 | impl From for Color { 84 | fn from(cc: CC) -> Self { 85 | match cc { 86 | CC::Reset => 0.into(), 87 | CC::Black => 0.into(), 88 | CC::DarkGrey => 8.into(), 89 | CC::Red => 9.into(), 90 | CC::DarkRed => 1.into(), 91 | CC::Green => 10.into(), 92 | CC::DarkGreen => 2.into(), 93 | CC::Yellow => 11.into(), 94 | CC::DarkYellow => 3.into(), 95 | CC::Blue => 12.into(), 96 | CC::DarkBlue => 4.into(), 97 | CC::Magenta => 13.into(), 98 | CC::DarkMagenta => 5.into(), 99 | CC::Cyan => 14.into(), 100 | CC::DarkCyan => 6.into(), 101 | CC::White => 15.into(), 102 | CC::Grey => 7.into(), 103 | CC::Rgb { r, g, b } => Color::Rgb(Rgb { r, g, b }), 104 | CC::AnsiValue(code) => code.into(), 105 | } 106 | } 107 | } 108 | 109 | #[cfg(feature = "crossterm")] 110 | impl Into for Color { 111 | fn into(self) -> CC { 112 | match self { 113 | Self::Ansi(AnsiColor { code }) => CC::AnsiValue(code), 114 | Self::Rgb(Rgb { r, g, b }) => CC::Rgb { r, g, b }, 115 | Self::Hsl(hsl) => { 116 | let Rgb { r, g, b } = hsl.to_rgb(); 117 | CC::Rgb { r, g, b } 118 | } 119 | } 120 | } 121 | } 122 | 123 | /// check going from ansi to rgb and back makes us fall on the first color 124 | #[test] 125 | fn test_ansi_to_rgb_to_ansi() { 126 | // we don't check the range 0..16 as it's made of colors 127 | // which are also in the 16..255 range 128 | for code in 16..=255 { 129 | let c1 = AnsiColor { code }; 130 | let c2 = c1.to_rgb(); 131 | let c3 = c2.to_ansi(); 132 | assert_eq!(c1, c3); 133 | } 134 | } 135 | /// check going from ansi to hsl and back makes us fall on the first color 136 | #[test] 137 | fn test_ansi_to_hsl_to_ansi() { 138 | // we don't check the range 0..16 as it's made of colors 139 | // which are also in the 16..255 range 140 | for code in 16..=255 { 141 | let c1 = AnsiColor { code }; 142 | let c2 = c1.to_hsl(); 143 | let c3 = c2.to_ansi(); 144 | assert_eq!(c1, c3); 145 | } 146 | } 147 | #[test] 148 | fn test_rgb_to_hsl() { 149 | assert!(Rgb::new(255, 0, 0).to_hsl().near(Hsl::new(0.0, 1.0, 0.5))); // red 150 | assert!(Rgb::new(255, 255, 0) 151 | .to_hsl() 152 | .near(Hsl::new(60.0, 1.0, 0.5))); // yellow 153 | assert!(Rgb::new(255, 255, 255) 154 | .to_hsl() 155 | .near(Hsl::new(0.0, 0.0, 1.0))); // white 156 | } 157 | /// check going from hsl to rgb and back makes us fall on the first color (or not too far) 158 | #[test] 159 | fn test_hsl_to_rgb_to_hsl() { 160 | let red = Hsl::new(0.0, 1.0, 0.5); 161 | let yellow = Hsl::new(60.0, 1.0, 0.5); 162 | let white = Hsl::new(0.0, 0.0, 1.0); 163 | assert!(red.to_rgb().to_hsl().near(red)); 164 | assert!(yellow.to_rgb().to_hsl().near(yellow)); 165 | assert!(white.to_rgb().to_hsl().near(white)); 166 | } 167 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | /// coolor error type 2 | pub enum CoolorError { 3 | InvalidHsl(f32, f32, f32), 4 | } 5 | -------------------------------------------------------------------------------- /src/hsl.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// HSL color 4 | #[derive(Clone, Copy, Debug, PartialEq)] 5 | pub struct Hsl { 6 | /// hue in `[0,360[` 7 | pub h: f32, 8 | /// saturation in `[0,1]` 9 | pub s: f32, 10 | /// luminosity in `[0,1]` 11 | pub l: f32, 12 | } 13 | 14 | impl Hsl { 15 | /// Create a new HSL color from its components 16 | pub fn new(h: f32, s: f32, l: f32) -> Self { 17 | debug_assert!(h >= 0.0 && h < 360.0); 18 | debug_assert!(s >= 0.0 && s <= 1.0); 19 | debug_assert!(l >= 0.0 && l <= 1.0); 20 | Self { h, s, l } 21 | } 22 | /// Create a new HSL color from its components, checking the ranges 23 | pub fn checked(h: f32, s: f32, l: f32) -> Result { 24 | if !(h >= 0.0 && h < 360.0 && s >= 0.0 && s <= 1.0 && l >= 0.0 && l <= 1.0) { 25 | Ok(Self { h, s, l }) 26 | } else { 27 | Err(CoolorError::InvalidHsl(h, s, l)) 28 | } 29 | } 30 | pub fn mix(c1: Self, w1: f32, c2: Self, w2: f32) -> Self { 31 | debug_assert!(w1 + w2 > 0.0); 32 | let h = if dist(c1.h, c2.h) > 180.0 { 33 | // the shortest path involve crossing Tau 34 | let (h1, h2) = if c1.h < c2.h { 35 | (c1.h + 360.0, c2.h) 36 | } else { 37 | (c1.h, c2.h + 360.0) 38 | }; 39 | ((w1 * h1 + w2 * h2) / (w1 + w2)) % 360.0 40 | } else { 41 | // direct way 42 | (w1 * c1.h + w2 * c2.h) / (w1 + w2) 43 | }; 44 | //let h = (w1*c1.h + w2*c2.h) / (w1+w2); 45 | let s = (w1 * c1.s + w2 * c2.s) / (w1 + w2); 46 | let l = (w1 * c1.l + w2 * c2.l) / (w1 + w2); 47 | Self { h, s, l } 48 | } 49 | /// Return the nearest ANSI color 50 | /// 51 | /// This is a slow function as it literally tries all 52 | /// ANSI colors and picks the nearest one. 53 | pub fn to_ansi(self) -> AnsiColor { 54 | let mut best = AnsiColor { code: 16 }; 55 | let mut smallest_distance: f32 = self.distance_to(best); 56 | for code in 17..=255 { 57 | let color = AnsiColor { code }; 58 | let distance = self.distance_to(color); 59 | if distance < smallest_distance { 60 | best = color; 61 | smallest_distance = distance; 62 | } 63 | } 64 | best 65 | } 66 | pub fn to_rgb(self) -> Rgb { 67 | let h = self.h / 360.0; 68 | let s = self.s; 69 | let l = self.l; 70 | let rgb = if s == 0.0 { 71 | (l, l, l) 72 | } else { 73 | let v2 = if l < 0.5 { 74 | l * (1.0 + s) 75 | } else { 76 | l + s - (s * l) 77 | }; 78 | let v1 = 2.0 * l - v2; 79 | ( 80 | hue_to_rgb_component(v1, v2, h + (1.0 / 3.0)), 81 | hue_to_rgb_component(v1, v2, h), 82 | hue_to_rgb_component(v1, v2, h - (1.0 / 3.0)), 83 | ) 84 | }; 85 | rgb.into() 86 | } 87 | pub fn delta_h(self, other: Hsl) -> f32 { 88 | dist(self.h, other.h).min(dist(self.h, 360.0)) // it's a circle, 0==360 89 | } 90 | pub fn delta_s(self, other: Hsl) -> f32 { 91 | dist(self.s, other.s) 92 | } 93 | pub fn delta_l(self, other: Hsl) -> f32 { 94 | dist(self.l, other.l) 95 | } 96 | /// tentatively perceptual distance between the two colors, 97 | /// except it's just as unscientific it can possibly be so 98 | /// check it looks good before trying ot use it, at least... 99 | pub fn distance_to>(self, other: H) -> f32 { 100 | let other: Hsl = other.into(); 101 | self.delta_h(other) / 360.0 + self.delta_s(other) + self.delta_l(other) 102 | } 103 | /// Tell whether it's about the same color 104 | /// 105 | /// There's no theory behind this function, it should not 106 | /// be used outside of unit tests 107 | pub fn near(self, other: Hsl) -> bool { 108 | self.distance_to(other) < 0.01 109 | } 110 | } 111 | 112 | impl From for Hsl { 113 | fn from(ansi: AnsiColor) -> Self { 114 | ansi.to_hsl() 115 | } 116 | } 117 | impl From for Hsl { 118 | fn from(rgb: Rgb) -> Self { 119 | rgb.to_hsl() 120 | } 121 | } 122 | 123 | fn hue_to_rgb_component(v1: f32, v2: f32, vh: f32) -> f32 { 124 | let vh = (vh + 1.0) % 1.0; 125 | if 6.0 * vh < 1.0 { 126 | (v1 + (v2 - v1) * 6.0 * vh).clamp(0.0, 1.0) 127 | } else if 2.0 * vh < 1.0 { 128 | v2 129 | } else if 3.0 * vh < 2.0 { 130 | (v1 + (v2 - v1) * ((2.0 / 3.0) - vh) * 6.0).clamp(0.0, 1.0) 131 | } else { 132 | v1 133 | } 134 | } 135 | 136 | fn dist(a: f32, b: f32) -> f32 { 137 | if a < b { 138 | b - a 139 | } else { 140 | a - b 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | Definition of ANSI, RGB and HSL color types and all the conversions between them. 4 | 5 | There are many other color conversion crates. 6 | This one may be useful when you're interested into 7 | 8 | - variations of an ANSI color for your TUI application, for example fading, lightening, darkening, with compatibility with terminals that don't support RGB. 9 | - translations of color schemes 10 | - automatic downgrading of RGB color schemes for non RGB terminals 11 | - automated building of harmonious color schemes with guarantees of contrast 12 | - etc. 13 | 14 | Be warned that the ANSI range is limited and that not all intuitive operations will give good results. 15 | 16 | The included example shows luminosity and saturation variants of all 240 ANSI colors, with all variants still ANSI colors. 17 | 18 | */ 19 | 20 | #![no_std] 21 | 22 | mod ansi; 23 | mod color; 24 | mod error; 25 | mod hsl; 26 | mod rgb; 27 | 28 | pub use { 29 | ansi::*, 30 | color::*, 31 | error::*, 32 | hsl::*, 33 | rgb::*, 34 | }; 35 | -------------------------------------------------------------------------------- /src/rgb.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// RGB color, with u8 components 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 | pub struct Rgb { 6 | /// red 7 | pub r: u8, 8 | /// green 9 | pub g: u8, 10 | /// blue 11 | pub b: u8, 12 | } 13 | 14 | impl Rgb { 15 | /// Create a new RGB color from its components 16 | pub const fn new(r: u8, g: u8, b: u8) -> Self { 17 | Self { r, g, b } 18 | } 19 | pub const fn is_grey(self) -> bool { 20 | self.r == self.g && self.g == self.b 21 | } 22 | #[inline] 23 | pub fn nearest_ansi_in_range(self, min: u8, max: u8) -> AnsiColor { 24 | let mut best = AnsiColor { code: min }; 25 | let mut smallest_distance: f32 = self.distance_to(best.to_rgb()); 26 | for code in min+1..=max { 27 | let color = AnsiColor { code }; 28 | let distance = self.distance_to(color.to_rgb()); 29 | if distance < smallest_distance { 30 | best = color; 31 | smallest_distance = distance; 32 | } 33 | } 34 | best 35 | } 36 | /// Return the nearest ANSI color 37 | /// 38 | /// The ansi->rgb->ansi round trip is guaranteed to 39 | /// always fall on the first color. 40 | pub fn to_ansi(self) -> AnsiColor { 41 | if self.r == self.g && self.g == self.b { 42 | AnsiColor { code: GREY_TO_ANSI[self.r as usize] } 43 | } else if self.r < 108 { 44 | if self.r < 41 { 45 | self.nearest_ansi_in_range(17, 51) 46 | } else { 47 | self.nearest_ansi_in_range(52, 87) 48 | } 49 | } else if self.r < 195 { 50 | if self.r < 151 { 51 | self.nearest_ansi_in_range(88, 123) 52 | } else { 53 | self.nearest_ansi_in_range(124, 159) 54 | } 55 | } else { 56 | if self.r < 235 { 57 | self.nearest_ansi_in_range(160, 195) 58 | } else { 59 | self.nearest_ansi_in_range(196, 230) 60 | } 61 | } 62 | } 63 | pub fn mix(c1: Self, w1: f32, c2: Self, w2: f32) -> Self { 64 | debug_assert!(w1 + w2 > 0.0); 65 | let (r1, g1, b1) = c1.parts(); 66 | let (r2, g2, b2) = c2.parts(); 67 | let r = (w1 * r1 + w2 * r2) / (w1 + w2); 68 | let g = (w1 * g1 + w2 * g2) / (w1 + w2); 69 | let b = (w1 * b1 + w2 * b2) / (w1 + w2); 70 | (r, g, b).into() 71 | } 72 | #[allow(clippy::float_cmp)] 73 | pub fn to_hsl(self) -> Hsl { 74 | let (r, g, b) = (self.rp(), self.gp(), self.bp()); 75 | let min = r.min(g).min(b); 76 | let max = r.max(g).max(b); 77 | 78 | let l = 0.5 * (max + min); 79 | 80 | if min == max { 81 | // gray level 82 | return Hsl::new(0.0, 0.0, l); 83 | } 84 | 85 | let h = if max == r { 86 | 60.0 * (g - b) / (max - min) 87 | } else if max == g { 88 | 60.0 * (b - r) / (max - min) + 120.0 89 | } else if max == b { 90 | 60.0 * (r - g) / (max - min) + 240.0 91 | } else { 92 | 0.0 93 | }; 94 | let h = (h + 360.0) % 360.0; 95 | 96 | let s = if 0.0 < l && l <= 0.5 { 97 | (max - min) / (2.0 * l) 98 | } else { 99 | (max - min) / (2.0 - 2.0 * l) 100 | }; 101 | 102 | Hsl { h, s, l } 103 | } 104 | /// red part in `[0,1]` 105 | pub fn rp(self) -> f32 { 106 | self.r as f32 / 256f32 107 | } 108 | /// green part in `[0,1]` 109 | pub fn gp(self) -> f32 { 110 | self.g as f32 / 256f32 111 | } 112 | /// blue part in `[0,1]` 113 | pub fn bp(self) -> f32 { 114 | self.b as f32 / 256f32 115 | } 116 | pub fn parts(self) -> (f32, f32, f32) { 117 | (self.rp(), self.gp(), self.bp()) 118 | } 119 | /// Compute the Luma value characterizing the "light" of the color, 120 | /// going from 0 (black) to 1 (white). 121 | /// 122 | /// Reference: 123 | pub fn luma(self) -> f32 { 124 | 0.2627 * self.rp() + 0.6780 * self.gp() + 0.0593 * self.bp() 125 | } 126 | /// tentatively perceptual distance between two RGB colors 127 | /// (adapted from the ansi_colours crate, by mina86, who adapted 128 | /// a formula found at https://www.compuphase.com/cmetric.htm) 129 | #[inline(always)] 130 | pub fn distance_to>(self, other: O) -> f32 { 131 | let other = other.into(); 132 | let r_sum = self.r as f32 + other.r as f32; 133 | let r = self.r as f32 - other.r as f32; 134 | let g = self.g as f32 - other.g as f32; 135 | let b = self.b as f32 - other.b as f32; 136 | (1024.0 + r_sum) * r * r + 2048.0 * g * g + (1534.0 - r_sum) * b * b 137 | } 138 | } 139 | 140 | pub fn r255(v: f32) -> u8 { 141 | (v * 255.0) as u8 142 | } 143 | 144 | impl From<(f32, f32, f32)> for Rgb { 145 | /// Convert from a (r,g,b) float tupples with components in [0,1[ 146 | fn from(c: (f32, f32, f32)) -> Self { 147 | debug_assert!(c.0 <= 1.0); 148 | debug_assert!(c.1 <= 1.0); 149 | debug_assert!(c.2 <= 1.0); 150 | Rgb::new(r255(c.0), r255(c.1), r255(c.2)) 151 | } 152 | } 153 | 154 | pub const GREY_TO_ANSI: &[u8] = &[ 155 | 16, 156 | 16, 157 | 16, 158 | 16, 159 | 16, 160 | 232, 161 | 232, 162 | 232, 163 | 232, 164 | 232, 165 | 232, 166 | 232, 167 | 232, 168 | 232, 169 | 233, 170 | 233, 171 | 233, 172 | 233, 173 | 233, 174 | 233, 175 | 233, 176 | 233, 177 | 233, 178 | 233, 179 | 234, 180 | 234, 181 | 234, 182 | 234, 183 | 234, 184 | 234, 185 | 234, 186 | 234, 187 | 234, 188 | 234, 189 | 235, 190 | 235, 191 | 235, 192 | 235, 193 | 235, 194 | 235, 195 | 235, 196 | 235, 197 | 235, 198 | 235, 199 | 236, 200 | 236, 201 | 236, 202 | 236, 203 | 236, 204 | 236, 205 | 236, 206 | 236, 207 | 236, 208 | 236, 209 | 237, 210 | 237, 211 | 237, 212 | 237, 213 | 237, 214 | 237, 215 | 237, 216 | 237, 217 | 237, 218 | 237, 219 | 238, 220 | 238, 221 | 238, 222 | 238, 223 | 238, 224 | 238, 225 | 238, 226 | 238, 227 | 238, 228 | 238, 229 | 239, 230 | 239, 231 | 239, 232 | 239, 233 | 239, 234 | 239, 235 | 239, 236 | 239, 237 | 239, 238 | 239, 239 | 240, 240 | 240, 241 | 240, 242 | 240, 243 | 240, 244 | 240, 245 | 240, 246 | 240, 247 | 59, 248 | 59, 249 | 59, 250 | 59, 251 | 241, 252 | 241, 253 | 241, 254 | 241, 255 | 242, 256 | 242, 257 | 242, 258 | 242, 259 | 242, 260 | 242, 261 | 242, 262 | 242, 263 | 242, 264 | 242, 265 | 242, 266 | 243, 267 | 243, 268 | 243, 269 | 243, 270 | 243, 271 | 243, 272 | 243, 273 | 243, 274 | 243, 275 | 243, 276 | 243, 277 | 243, 278 | 243, 279 | 244, 280 | 244, 281 | 244, 282 | 244, 283 | 244, 284 | 244, 285 | 244, 286 | 244, 287 | 102, 288 | 102, 289 | 102, 290 | 102, 291 | 102, 292 | 245, 293 | 245, 294 | 245, 295 | 245, 296 | 245, 297 | 245, 298 | 245, 299 | 246, 300 | 246, 301 | 246, 302 | 246, 303 | 246, 304 | 246, 305 | 246, 306 | 246, 307 | 246, 308 | 246, 309 | 247, 310 | 247, 311 | 247, 312 | 247, 313 | 247, 314 | 247, 315 | 247, 316 | 247, 317 | 247, 318 | 247, 319 | 248, 320 | 248, 321 | 248, 322 | 248, 323 | 248, 324 | 248, 325 | 248, 326 | 248, 327 | 145, 328 | 145, 329 | 145, 330 | 145, 331 | 145, 332 | 249, 333 | 249, 334 | 249, 335 | 249, 336 | 249, 337 | 249, 338 | 249, 339 | 250, 340 | 250, 341 | 250, 342 | 250, 343 | 250, 344 | 250, 345 | 250, 346 | 250, 347 | 250, 348 | 250, 349 | 251, 350 | 251, 351 | 251, 352 | 251, 353 | 251, 354 | 251, 355 | 251, 356 | 251, 357 | 251, 358 | 251, 359 | 252, 360 | 252, 361 | 252, 362 | 252, 363 | 252, 364 | 252, 365 | 252, 366 | 252, 367 | 188, 368 | 188, 369 | 188, 370 | 188, 371 | 188, 372 | 253, 373 | 253, 374 | 253, 375 | 253, 376 | 253, 377 | 253, 378 | 253, 379 | 254, 380 | 254, 381 | 254, 382 | 254, 383 | 254, 384 | 254, 385 | 254, 386 | 254, 387 | 254, 388 | 254, 389 | 255, 390 | 255, 391 | 255, 392 | 255, 393 | 255, 394 | 255, 395 | 255, 396 | 255, 397 | 255, 398 | 255, 399 | 255, 400 | 255, 401 | 255, 402 | 231, 403 | 231, 404 | 231, 405 | 231, 406 | 231, 407 | 231, 408 | 231, 409 | 231, 410 | 231, 411 | ]; 412 | 413 | 414 | --------------------------------------------------------------------------------