doryen_extra/
color.rs

1/* BSD 3-Clause License
2 *
3 * Copyright © 2019, Alexander Krivács Schrøder <alexschrod@gmail.com>.
4 * Copyright © 2008-2019, Jice and the libtcod contributors.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 *    this list of conditions and the following disclaimer in the documentation
15 *    and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 *    contributors may be used to endorse or promote products derived from
19 *    this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34//! # Color representation and operations.
35//!
36//! A color is defined by its red, green and blue components in the range 0 to 255.
37//! You can use the following predefined colors (hover over a color to see its full name and
38//! RGB values:
39//!
40//! <table class="color">
41//! <tbody><tr><td></td><th colspan="8">STANDARD COLORS</th></tr>
42//! <tr><td></td><td>DESATURATED</td><td>LIGHTEST</td><td>LIGHTER</td><td>LIGHT</td><td>NORMAL</td><td>DARK</td><td>DARKER</td><td>DARKEST</td></tr>
43//! <tr><td>RED</td><td title="DESATURATED_RED (128, 64, 64)" style="background-color: rgb(128, 64, 64); --darkreader-inline-bgcolor:#663333;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_RED (255, 191, 191)" style="background-color: rgb(255, 191, 191); --darkreader-inline-bgcolor:#590000;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_RED (255, 166, 166)" style="background-color: rgb(255, 166, 166); --darkreader-inline-bgcolor:#680000;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_RED (255, 115, 115)" style="background-color: rgb(255, 115, 115); --darkreader-inline-bgcolor:#870000;" data-darkreader-inline-bgcolor=""></td><td title="RED (255, 0, 0)" style="background-color: rgb(255, 0, 0); --darkreader-inline-bgcolor:#cc0000;" data-darkreader-inline-bgcolor=""></td><td title="DARK_RED (191, 0, 0)" style="background-color: rgb(191, 0, 0); --darkreader-inline-bgcolor:#bf0000;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_RED (128, 0, 0)" style="background-color: rgb(128, 0, 0); --darkreader-inline-bgcolor:#800000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_RED (64, 0, 0)" style="background-color: rgb(64, 0, 0); --darkreader-inline-bgcolor:#400000;" data-darkreader-inline-bgcolor=""></td></tr>
44//! <tr><td>FLAME</td><td title="DESATURATED_FLAME (128, 80, 64)" style="background-color: rgb(128, 80, 64); --darkreader-inline-bgcolor:#664033;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_FLAME (255, 207, 191)" style="background-color: rgb(255, 207, 191); --darkreader-inline-bgcolor:#591600;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_FLAME (255, 188, 166)" style="background-color: rgb(255, 188, 166); --darkreader-inline-bgcolor:#681a00;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_FLAME (255, 149, 115)" style="background-color: rgb(255, 149, 115); --darkreader-inline-bgcolor:#872100;" data-darkreader-inline-bgcolor=""></td><td title="FLAME (255, 63, 0)" style="background-color: rgb(255, 63, 0); --darkreader-inline-bgcolor:#cc3200;" data-darkreader-inline-bgcolor=""></td><td title="DARK_FLAME (191, 47, 0)" style="background-color: rgb(191, 47, 0); --darkreader-inline-bgcolor:#bf2f00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_FLAME (128, 32, 0)" style="background-color: rgb(128, 32, 0); --darkreader-inline-bgcolor:#802000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_FLAME (64, 16, 0)" style="background-color: rgb(64, 16, 0); --darkreader-inline-bgcolor:#401000;" data-darkreader-inline-bgcolor=""></td></tr>
45//! <tr><td>ORANGE</td><td title="DESATURATED_ORANGE (128, 96, 64)" style="background-color: rgb(128, 96, 64); --darkreader-inline-bgcolor:#664d33;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_ORANGE (255, 223, 191)" style="background-color: rgb(255, 223, 191); --darkreader-inline-bgcolor:#592d00;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_ORANGE (255, 210, 166)" style="background-color: rgb(255, 210, 166); --darkreader-inline-bgcolor:#683400;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_ORANGE (255, 185, 115)" style="background-color: rgb(255, 185, 115); --darkreader-inline-bgcolor:#874300;" data-darkreader-inline-bgcolor=""></td><td title="ORANGE (255, 127, 0)" style="background-color: rgb(255, 127, 0); --darkreader-inline-bgcolor:#cc6600;" data-darkreader-inline-bgcolor=""></td><td title="DARK_ORANGE (191, 95, 0)" style="background-color: rgb(191, 95, 0); --darkreader-inline-bgcolor:#bf5f00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_ORANGE (128, 64, 0)" style="background-color: rgb(128, 64, 0); --darkreader-inline-bgcolor:#804000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_ORANGE (64, 32, 0)" style="background-color: rgb(64, 32, 0); --darkreader-inline-bgcolor:#402000;" data-darkreader-inline-bgcolor=""></td></tr>
46//! <tr><td>AMBER</td><td title="DESATURATED_AMBER (128, 112, 64)" style="background-color: rgb(128, 112, 64); --darkreader-inline-bgcolor:#665933;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_AMBER (255, 239, 191)" style="background-color: rgb(255, 239, 191); --darkreader-inline-bgcolor:#594300;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_AMBER (255, 233, 166)" style="background-color: rgb(255, 233, 166); --darkreader-inline-bgcolor:#684f00;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_AMBER (255, 220, 115)" style="background-color: rgb(255, 220, 115); --darkreader-inline-bgcolor:#876500;" data-darkreader-inline-bgcolor=""></td><td title="AMBER (255, 191, 0)" style="background-color: rgb(255, 191, 0); --darkreader-inline-bgcolor:#cc9900;" data-darkreader-inline-bgcolor=""></td><td title="DARK_AMBER (191, 143, 0)" style="background-color: rgb(191, 143, 0); --darkreader-inline-bgcolor:#bf8f00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_AMBER (128, 96, 0)" style="background-color: rgb(128, 96, 0); --darkreader-inline-bgcolor:#806000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_AMBER (64, 48, 0)" style="background-color: rgb(64, 48, 0); --darkreader-inline-bgcolor:#403000;" data-darkreader-inline-bgcolor=""></td></tr>
47//! <tr><td>YELLOW</td><td title="DESATURATED_YELLOW (128, 128, 64)" style="background-color: rgb(128, 128, 64); --darkreader-inline-bgcolor:#666633;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_YELLOW (255, 255, 191)" style="background-color: rgb(255, 255, 191); --darkreader-inline-bgcolor:#595900;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_YELLOW (255, 255, 166)" style="background-color: rgb(255, 255, 166); --darkreader-inline-bgcolor:#686800;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_YELLOW (255, 255, 115)" style="background-color: rgb(255, 255, 115); --darkreader-inline-bgcolor:#878700;" data-darkreader-inline-bgcolor=""></td><td title="YELLOW (255, 255, 0)" style="background-color: rgb(255, 255, 0); --darkreader-inline-bgcolor:#cccc00;" data-darkreader-inline-bgcolor=""></td><td title="DARK_YELLOW (191, 191, 0)" style="background-color: rgb(191, 191, 0); --darkreader-inline-bgcolor:#bfbf00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_YELLOW (128, 128, 0)" style="background-color: rgb(128, 128, 0); --darkreader-inline-bgcolor:#808000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_YELLOW (64, 64, 0)" style="background-color: rgb(64, 64, 0); --darkreader-inline-bgcolor:#404000;" data-darkreader-inline-bgcolor=""></td></tr>
48//! <tr><td>LIME</td><td title="DESATURATED_LIME (112, 128, 64)" style="background-color: rgb(112, 128, 64); --darkreader-inline-bgcolor:#596633;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_LIME (239, 255, 191)" style="background-color: rgb(239, 255, 191); --darkreader-inline-bgcolor:#435900;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_LIME (233, 255, 166)" style="background-color: rgb(233, 255, 166); --darkreader-inline-bgcolor:#4f6800;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_LIME (220, 255, 115)" style="background-color: rgb(220, 255, 115); --darkreader-inline-bgcolor:#658700;" data-darkreader-inline-bgcolor=""></td><td title="LIME (191, 255, 0)" style="background-color: rgb(191, 255, 0); --darkreader-inline-bgcolor:#99cc00;" data-darkreader-inline-bgcolor=""></td><td title="DARK_LIME (143, 191, 0)" style="background-color: rgb(143, 191, 0); --darkreader-inline-bgcolor:#8fbf00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_LIME (96, 128, 0)" style="background-color: rgb(96, 128, 0); --darkreader-inline-bgcolor:#608000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_LIME (48, 64, 0)" style="background-color: rgb(48, 64, 0); --darkreader-inline-bgcolor:#304000;" data-darkreader-inline-bgcolor=""></td></tr>
49//! <tr><td>CHARTREUSE</td><td title="DESATURATED_CHARTREUSE (96, 128, 64)" style="background-color: rgb(96, 128, 64); --darkreader-inline-bgcolor:#4d6633;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_CHARTREUSE (223, 255, 191)" style="background-color: rgb(223, 255, 191); --darkreader-inline-bgcolor:#2d5900;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_CHARTREUSE (210, 255, 166)" style="background-color: rgb(210, 255, 166); --darkreader-inline-bgcolor:#346800;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_CHARTREUSE (185, 255, 115)" style="background-color: rgb(185, 255, 115); --darkreader-inline-bgcolor:#448700;" data-darkreader-inline-bgcolor=""></td><td title="CHARTREUSE (127, 255, 0)" style="background-color: rgb(127, 255, 0); --darkreader-inline-bgcolor:#66cc00;" data-darkreader-inline-bgcolor=""></td><td title="DARK_CHARTREUSE (95, 191, 0)" style="background-color: rgb(95, 191, 0); --darkreader-inline-bgcolor:#5fbf00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_CHARTREUSE (64, 128, 0)" style="background-color: rgb(64, 128, 0); --darkreader-inline-bgcolor:#408000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_CHARTREUSE (32, 64, 0)" style="background-color: rgb(32, 64, 0); --darkreader-inline-bgcolor:#204000;" data-darkreader-inline-bgcolor=""></td></tr>
50//! <tr><td>GREEN</td><td title="DESATURATED_GREEN (64, 128, 64)" style="background-color: rgb(64, 128, 64); --darkreader-inline-bgcolor:#336633;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_GREEN (191, 255, 191)" style="background-color: rgb(191, 255, 191); --darkreader-inline-bgcolor:#005900;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_GREEN (166, 255, 166)" style="background-color: rgb(166, 255, 166); --darkreader-inline-bgcolor:#006800;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_GREEN (115, 255, 115)" style="background-color: rgb(115, 255, 115); --darkreader-inline-bgcolor:#008700;" data-darkreader-inline-bgcolor=""></td><td title="GREEN (0, 255, 0)" style="background-color: rgb(0, 255, 0); --darkreader-inline-bgcolor:#00cc00;" data-darkreader-inline-bgcolor=""></td><td title="DARK_GREEN (0, 191, 0)" style="background-color: rgb(0, 191, 0); --darkreader-inline-bgcolor:#00bf00;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_GREEN (0, 128, 0)" style="background-color: rgb(0, 128, 0); --darkreader-inline-bgcolor:#008000;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_GREEN (0, 64, 0)" style="background-color: rgb(0, 64, 0); --darkreader-inline-bgcolor:#004000;" data-darkreader-inline-bgcolor=""></td></tr>
51//! <tr><td>SEA</td><td title="DESATURATED_SEA (64, 128, 96)" style="background-color: rgb(64, 128, 96); --darkreader-inline-bgcolor:#33664d;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_SEA (191, 255, 223)" style="background-color: rgb(191, 255, 223); --darkreader-inline-bgcolor:#00592d;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_SEA (166, 255, 210)" style="background-color: rgb(166, 255, 210); --darkreader-inline-bgcolor:#006834;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_SEA (115, 255, 185)" style="background-color: rgb(115, 255, 185); --darkreader-inline-bgcolor:#008744;" data-darkreader-inline-bgcolor=""></td><td title="SEA (0, 255, 127)" style="background-color: rgb(0, 255, 127); --darkreader-inline-bgcolor:#00cc66;" data-darkreader-inline-bgcolor=""></td><td title="DARK_SEA (0, 191, 95)" style="background-color: rgb(0, 191, 95); --darkreader-inline-bgcolor:#00bf5f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_SEA (0, 128, 64)" style="background-color: rgb(0, 128, 64); --darkreader-inline-bgcolor:#008040;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_SEA (0, 64, 32)" style="background-color: rgb(0, 64, 32); --darkreader-inline-bgcolor:#004020;" data-darkreader-inline-bgcolor=""></td></tr>
52//! <tr><td>TURQUOISE</td><td title="DESATURATED_TURQUOISE (64, 128, 112)" style="background-color: rgb(64, 128, 112); --darkreader-inline-bgcolor:#336659;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_TURQUOISE (191, 255, 239)" style="background-color: rgb(191, 255, 239); --darkreader-inline-bgcolor:#005943;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_TURQUOISE (166, 255, 233)" style="background-color: rgb(166, 255, 233); --darkreader-inline-bgcolor:#00684f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_TURQUOISE (115, 255, 220)" style="background-color: rgb(115, 255, 220); --darkreader-inline-bgcolor:#008765;" data-darkreader-inline-bgcolor=""></td><td title="TURQUOISE (0, 255, 191)" style="background-color: rgb(0, 255, 191); --darkreader-inline-bgcolor:#00cc99;" data-darkreader-inline-bgcolor=""></td><td title="DARK_TURQUOISE (0, 191, 143)" style="background-color: rgb(0, 191, 143); --darkreader-inline-bgcolor:#00bf8f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_TURQUOISE (0, 128, 96)" style="background-color: rgb(0, 128, 96); --darkreader-inline-bgcolor:#008060;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_TURQUOISE (0, 64, 48)" style="background-color: rgb(0, 64, 48); --darkreader-inline-bgcolor:#004030;" data-darkreader-inline-bgcolor=""></td></tr>
53//! <tr><td>CYAN</td><td title="DESATURATED_CYAN (64, 128, 128)" style="background-color: rgb(64, 128, 128); --darkreader-inline-bgcolor:#336666;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_CYAN (191, 255, 255)" style="background-color: rgb(191, 255, 255); --darkreader-inline-bgcolor:#005959;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_CYAN (166, 255, 255)" style="background-color: rgb(166, 255, 255); --darkreader-inline-bgcolor:#006868;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_CYAN (115, 255, 255)" style="background-color: rgb(115, 255, 255); --darkreader-inline-bgcolor:#008787;" data-darkreader-inline-bgcolor=""></td><td title="CYAN (0, 255, 255)" style="background-color: rgb(0, 255, 255); --darkreader-inline-bgcolor:#00cccc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_CYAN (0, 191, 191)" style="background-color: rgb(0, 191, 191); --darkreader-inline-bgcolor:#00bfbf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_CYAN (0, 128, 128)" style="background-color: rgb(0, 128, 128); --darkreader-inline-bgcolor:#008080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_CYAN (0, 64, 64)" style="background-color: rgb(0, 64, 64); --darkreader-inline-bgcolor:#004040;" data-darkreader-inline-bgcolor=""></td></tr>
54//! <tr><td>SKY</td><td title="DESATURATED_SKY (64, 112, 128)" style="background-color: rgb(64, 112, 128); --darkreader-inline-bgcolor:#335966;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_SKY (191, 239, 255)" style="background-color: rgb(191, 239, 255); --darkreader-inline-bgcolor:#004359;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_SKY (166, 233, 255)" style="background-color: rgb(166, 233, 255); --darkreader-inline-bgcolor:#004f68;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_SKY (115, 220, 255)" style="background-color: rgb(115, 220, 255); --darkreader-inline-bgcolor:#006587;" data-darkreader-inline-bgcolor=""></td><td title="SKY (0, 191, 255)" style="background-color: rgb(0, 191, 255); --darkreader-inline-bgcolor:#0099cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_SKY (0, 143, 191)" style="background-color: rgb(0, 143, 191); --darkreader-inline-bgcolor:#008fbf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_SKY (0, 96, 128)" style="background-color: rgb(0, 96, 128); --darkreader-inline-bgcolor:#006080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_SKY (0, 48, 64)" style="background-color: rgb(0, 48, 64); --darkreader-inline-bgcolor:#003040;" data-darkreader-inline-bgcolor=""></td></tr>
55//! <tr><td>AZURE</td><td title="DESATURATED_AZURE (64, 96, 128)" style="background-color: rgb(64, 96, 128); --darkreader-inline-bgcolor:#334d66;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_AZURE (191, 223, 255)" style="background-color: rgb(191, 223, 255); --darkreader-inline-bgcolor:#2a2d2f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_AZURE (166, 210, 255)" style="background-color: rgb(166, 210, 255); --darkreader-inline-bgcolor:#323537;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_AZURE (115, 185, 255)" style="background-color: rgb(115, 185, 255); --darkreader-inline-bgcolor:#004487;" data-darkreader-inline-bgcolor=""></td><td title="AZURE (0, 127, 255)" style="background-color: rgb(0, 127, 255); --darkreader-inline-bgcolor:#0066cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_AZURE (0, 95, 191)" style="background-color: rgb(0, 95, 191); --darkreader-inline-bgcolor:#005fbf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_AZURE (0, 64, 128)" style="background-color: rgb(0, 64, 128); --darkreader-inline-bgcolor:#004080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_AZURE (0, 32, 64)" style="background-color: rgb(0, 32, 64); --darkreader-inline-bgcolor:#002040;" data-darkreader-inline-bgcolor=""></td></tr>
56//! <tr><td>BLUE</td><td title="DESATURATED_BLUE (64, 64, 128)" style="background-color: rgb(64, 64, 128); --darkreader-inline-bgcolor:#333366;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_BLUE (191, 191, 255)" style="background-color: rgb(191, 191, 255); --darkreader-inline-bgcolor:#2a2d2f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_BLUE (166, 166, 255)" style="background-color: rgb(166, 166, 255); --darkreader-inline-bgcolor:#323537;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_BLUE (115, 115, 255)" style="background-color: rgb(115, 115, 255); --darkreader-inline-bgcolor:#000087;" data-darkreader-inline-bgcolor=""></td><td title="BLUE (0, 0, 255)" style="background-color: rgb(0, 0, 255); --darkreader-inline-bgcolor:#0000cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_BLUE (0, 0, 191)" style="background-color: rgb(0, 0, 191); --darkreader-inline-bgcolor:#0000bf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_BLUE (0, 0, 128)" style="background-color: rgb(0, 0, 128); --darkreader-inline-bgcolor:#000080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_BLUE (0, 0, 64)" style="background-color: rgb(0, 0, 64); --darkreader-inline-bgcolor:#000040;" data-darkreader-inline-bgcolor=""></td></tr>
57//! <tr><td>HAN</td><td title="DESATURATED_HAN (80, 64, 128)" style="background-color: rgb(80, 64, 128); --darkreader-inline-bgcolor:#403366;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_HAN (207, 191, 255)" style="background-color: rgb(207, 191, 255); --darkreader-inline-bgcolor:#2a2d2f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_HAN (188, 166, 255)" style="background-color: rgb(188, 166, 255); --darkreader-inline-bgcolor:#323537;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_HAN (149, 115, 255)" style="background-color: rgb(149, 115, 255); --darkreader-inline-bgcolor:#210087;" data-darkreader-inline-bgcolor=""></td><td title="HAN (63, 0, 255)" style="background-color: rgb(63, 0, 255); --darkreader-inline-bgcolor:#3200cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_HAN (47, 0, 191)" style="background-color: rgb(47, 0, 191); --darkreader-inline-bgcolor:#2f00bf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_HAN (32, 0, 128)" style="background-color: rgb(32, 0, 128); --darkreader-inline-bgcolor:#200080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_HAN (16, 0, 64)" style="background-color: rgb(16, 0, 64); --darkreader-inline-bgcolor:#100040;" data-darkreader-inline-bgcolor=""></td></tr>
58//! <tr><td>VIOLET</td><td title="DESATURATED_VIOLET (96, 64, 128)" style="background-color: rgb(96, 64, 128); --darkreader-inline-bgcolor:#4d3366;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_VIOLET (223, 191, 255)" style="background-color: rgb(223, 191, 255); --darkreader-inline-bgcolor:#2a2d2f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_VIOLET (210, 166, 255)" style="background-color: rgb(210, 166, 255); --darkreader-inline-bgcolor:#323537;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_VIOLET (185, 115, 255)" style="background-color: rgb(185, 115, 255); --darkreader-inline-bgcolor:#440087;" data-darkreader-inline-bgcolor=""></td><td title="VIOLET (127, 0, 255)" style="background-color: rgb(127, 0, 255); --darkreader-inline-bgcolor:#6600cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_VIOLET (95, 0, 191)" style="background-color: rgb(95, 0, 191); --darkreader-inline-bgcolor:#5f00bf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_VIOLET (64, 0, 128)" style="background-color: rgb(64, 0, 128); --darkreader-inline-bgcolor:#400080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_VIOLET (32, 0, 64)" style="background-color: rgb(32, 0, 64); --darkreader-inline-bgcolor:#200040;" data-darkreader-inline-bgcolor=""></td></tr>
59//! <tr><td>PURPLE</td><td title="DESATURATED_PURPLE (111, 64, 128)" style="background-color: rgb(111, 64, 128); --darkreader-inline-bgcolor:#583366;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_PURPLE (239, 191, 255)" style="background-color: rgb(239, 191, 255); --darkreader-inline-bgcolor:#430059;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_PURPLE (233, 166, 255)" style="background-color: rgb(233, 166, 255); --darkreader-inline-bgcolor:#4f0068;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_PURPLE (220, 115, 255)" style="background-color: rgb(220, 115, 255); --darkreader-inline-bgcolor:#650087;" data-darkreader-inline-bgcolor=""></td><td title="PURPLE (191, 0, 255)" style="background-color: rgb(191, 0, 255); --darkreader-inline-bgcolor:#9900cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_PURPLE (143, 0, 191)" style="background-color: rgb(143, 0, 191); --darkreader-inline-bgcolor:#8f00bf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_PURPLE (95, 0, 128)" style="background-color: rgb(95, 0, 128); --darkreader-inline-bgcolor:#5f0080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_PURPLE (48, 0, 64)" style="background-color: rgb(48, 0, 64); --darkreader-inline-bgcolor:#300040;" data-darkreader-inline-bgcolor=""></td></tr>
60//! <tr><td>FUCHSIA</td><td title="DESATURATED_FUCHSIA (128, 64, 128)" style="background-color: rgb(128, 64, 128); --darkreader-inline-bgcolor:#663366;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_FUCHSIA (255, 191, 255)" style="background-color: rgb(255, 191, 255); --darkreader-inline-bgcolor:#590059;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_FUCHSIA (255, 166, 255)" style="background-color: rgb(255, 166, 255); --darkreader-inline-bgcolor:#680068;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_FUCHSIA (255, 115, 255)" style="background-color: rgb(255, 115, 255); --darkreader-inline-bgcolor:#870087;" data-darkreader-inline-bgcolor=""></td><td title="FUCHSIA (255, 0, 255)" style="background-color: rgb(255, 0, 255); --darkreader-inline-bgcolor:#cc00cc;" data-darkreader-inline-bgcolor=""></td><td title="DARK_FUCHSIA (191, 0, 191)" style="background-color: rgb(191, 0, 191); --darkreader-inline-bgcolor:#bf00bf;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_FUCHSIA (128, 0, 128)" style="background-color: rgb(128, 0, 128); --darkreader-inline-bgcolor:#800080;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_FUCHSIA (64, 0, 64)" style="background-color: rgb(64, 0, 64); --darkreader-inline-bgcolor:#400040;" data-darkreader-inline-bgcolor=""></td></tr>
61//! <tr><td>MAGENTA</td><td title="DESATURATED_MAGENTA (128, 64, 111)" style="background-color: rgb(128, 64, 111); --darkreader-inline-bgcolor:#663358;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_MAGENTA (255, 191, 239)" style="background-color: rgb(255, 191, 239); --darkreader-inline-bgcolor:#590043;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_MAGENTA (255, 166, 233)" style="background-color: rgb(255, 166, 233); --darkreader-inline-bgcolor:#68004f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_MAGENTA (255, 115, 220)" style="background-color: rgb(255, 115, 220); --darkreader-inline-bgcolor:#870065;" data-darkreader-inline-bgcolor=""></td><td title="MAGENTA (255, 0, 191)" style="background-color: rgb(255, 0, 191); --darkreader-inline-bgcolor:#cc0099;" data-darkreader-inline-bgcolor=""></td><td title="DARK_MAGENTA (191, 0, 143)" style="background-color: rgb(191, 0, 143); --darkreader-inline-bgcolor:#bf008f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_MAGENTA (128, 0, 95)" style="background-color: rgb(128, 0, 95); --darkreader-inline-bgcolor:#80005f;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_MAGENTA (64, 0, 48)" style="background-color: rgb(64, 0, 48); --darkreader-inline-bgcolor:#400030;" data-darkreader-inline-bgcolor=""></td></tr>
62//! <tr><td>PINK</td><td title="DESATURATED_PINK (128, 64, 96)" style="background-color: rgb(128, 64, 96); --darkreader-inline-bgcolor:#66334d;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_PINK (255, 191, 223)" style="background-color: rgb(255, 191, 223); --darkreader-inline-bgcolor:#59002d;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_PINK (255, 166, 210)" style="background-color: rgb(255, 166, 210); --darkreader-inline-bgcolor:#680034;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_PINK (255, 115, 185)" style="background-color: rgb(255, 115, 185); --darkreader-inline-bgcolor:#870044;" data-darkreader-inline-bgcolor=""></td><td title="PINK (255, 0, 127)" style="background-color: rgb(255, 0, 127); --darkreader-inline-bgcolor:#cc0066;" data-darkreader-inline-bgcolor=""></td><td title="DARK_PINK (191, 0, 95)" style="background-color: rgb(191, 0, 95); --darkreader-inline-bgcolor:#bf005f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_PINK (128, 0, 64)" style="background-color: rgb(128, 0, 64); --darkreader-inline-bgcolor:#800040;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_PINK (64, 0, 32)" style="background-color: rgb(64, 0, 32); --darkreader-inline-bgcolor:#400020;" data-darkreader-inline-bgcolor=""></td></tr>
63//! <tr><td>CRIMSON</td><td title="DESATURATED_CRIMSON (128, 64, 79)" style="background-color: rgb(128, 64, 79); --darkreader-inline-bgcolor:#66333f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTEST_CRIMSON (255, 191, 207)" style="background-color: rgb(255, 191, 207); --darkreader-inline-bgcolor:#590016;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_CRIMSON (255, 166, 188)" style="background-color: rgb(255, 166, 188); --darkreader-inline-bgcolor:#68001a;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_CRIMSON (255, 115, 149)" style="background-color: rgb(255, 115, 149); --darkreader-inline-bgcolor:#870021;" data-darkreader-inline-bgcolor=""></td><td title="CRIMSON (255, 0, 63)" style="background-color: rgb(255, 0, 63); --darkreader-inline-bgcolor:#cc0032;" data-darkreader-inline-bgcolor=""></td><td title="DARK_CRIMSON (191, 0, 47)" style="background-color: rgb(191, 0, 47); --darkreader-inline-bgcolor:#bf002f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_CRIMSON (128, 0, 31)" style="background-color: rgb(128, 0, 31); --darkreader-inline-bgcolor:#80001f;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_CRIMSON (64, 0, 16)" style="background-color: rgb(64, 0, 16); --darkreader-inline-bgcolor:#400010;" data-darkreader-inline-bgcolor=""></td></tr>
64//! <tr><td></td><th colspan="8">METALLIC COLORS</th></tr>
65//! <tr><td>BRASS</td><td title="BRASS (191, 151, 96)" style="background-color: rgb(191, 151, 96); --darkreader-inline-bgcolor:#684e2a;" data-darkreader-inline-bgcolor=""></td></tr>
66//! <tr><td>COPPER</td><td title="COPPER (196, 136, 124)" style="background-color: rgb(196, 136, 124); --darkreader-inline-bgcolor:#593028;" data-darkreader-inline-bgcolor=""></td></tr>
67//! <tr><td>GOLD</td><td title="GOLD (229, 191, 0)" style="background-color: rgb(229, 191, 0); --darkreader-inline-bgcolor:#ccaa00;" data-darkreader-inline-bgcolor=""></td></tr>
68//! <tr><td>SILVER</td><td title="SILVER (203, 203, 203)" style="background-color: rgb(203, 203, 203); --darkreader-inline-bgcolor:#27292b;" data-darkreader-inline-bgcolor=""></td></tr>
69//! <tr><td></td><th colspan="8">MISCELLANEOUS COLORS</th></tr>
70//! <tr><td>CELADON</td><td title="CELADON (172, 255, 175)" style="background-color: rgb(172, 255, 175); --darkreader-inline-bgcolor:#006504;" data-darkreader-inline-bgcolor=""></td></tr>
71//! <tr><td>PEACH</td><td title="PEACH (255, 159, 127)" style="background-color: rgb(255, 159, 127); --darkreader-inline-bgcolor:#802000;" data-darkreader-inline-bgcolor=""></td></tr>
72//! <tr><td></td><th colspan="8">GRAYSCALE &amp; SEPIA</th></tr>
73//! <tr><td colspan="2">&nbsp;</td><td>LIGHTEST</td><td>LIGHTER</td><td>LIGHT</td><td>NORMAL</td><td>DARK</td><td>DARKER</td><td>DARKEST</td></tr>
74//! <tr><td>GRAY</td><td>&nbsp;</td><td title="LIGHTEST_GRAY (223, 223, 223)" style="background-color: rgb(223, 223, 223); --darkreader-inline-bgcolor:#212325;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_GRAY (191, 191, 191)" style="background-color: rgb(191, 191, 191); --darkreader-inline-bgcolor:#2a2d2f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_GRAY (159, 159, 159)" style="background-color: rgb(159, 159, 159); --darkreader-inline-bgcolor:#343739;" data-darkreader-inline-bgcolor=""></td><td title="GRAY (127, 127, 127)" style="background-color: rgb(127, 127, 127); --darkreader-inline-bgcolor:#3d4043;" data-darkreader-inline-bgcolor=""></td><td title="DARK_GRAY (95, 95, 95)" style="background-color: rgb(95, 95, 95); --darkreader-inline-bgcolor:#3d4043;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_GRAY (63, 63, 63)" style="background-color: rgb(63, 63, 63); --darkreader-inline-bgcolor:#3c4042;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_GRAY (31, 31, 31)" style="background-color: rgb(31, 31, 31); --darkreader-inline-bgcolor:#1d1f21;" data-darkreader-inline-bgcolor=""></td></tr>
75//! <tr><td>SEPIA</td><td>&nbsp;</td><td title="LIGHTEST_SEPIA (222, 211, 195)" style="background-color: rgb(222, 211, 195); --darkreader-inline-bgcolor:#382e1f;" data-darkreader-inline-bgcolor=""></td><td title="LIGHTER_SEPIA (191, 171, 143)" style="background-color: rgb(191, 171, 143); --darkreader-inline-bgcolor:#4b3e2b;" data-darkreader-inline-bgcolor=""></td><td title="LIGHT_SEPIA (158, 134, 100)" style="background-color: rgb(158, 134, 100); --darkreader-inline-bgcolor:#594b37;" data-darkreader-inline-bgcolor=""></td><td title="SEPIA (127, 101, 63)" style="background-color: rgb(127, 101, 63); --darkreader-inline-bgcolor:#665133;" data-darkreader-inline-bgcolor=""></td><td title="DARK_SEPIA (94, 75, 47)" style="background-color: rgb(94, 75, 47); --darkreader-inline-bgcolor:#5e4b2f;" data-darkreader-inline-bgcolor=""></td><td title="DARKER_SEPIA (63, 50, 31)" style="background-color: rgb(63, 50, 31); --darkreader-inline-bgcolor:#3f321f;" data-darkreader-inline-bgcolor=""></td><td title="DARKEST_SEPIA (31, 24, 15)" style="background-color: rgb(31, 24, 15); --darkreader-inline-bgcolor:#1f180f;" data-darkreader-inline-bgcolor=""></td></tr><tr><td></td><th colspan="8">BLACK AND WHITE</th></tr>
76//! <tr><td>BLACK</td><td title="BLACK (0, 0, 0)" style="background-color: rgb(0, 0, 0); --darkreader-inline-bgcolor:#000000;" data-darkreader-inline-bgcolor=""></td></tr>
77//! <tr><td>WHITE</td><td title="WHITE (255, 255, 255)" style="background-color: rgb(255, 255, 255); --darkreader-inline-bgcolor:#181a1b;" data-darkreader-inline-bgcolor=""></td></tr>
78//! </tbody></table>
79
80use crate::util::FloorRem;
81use std::ops::{Add, Mul, Sub};
82
83pub use Color as Colour;
84
85/// A struct representing a 24-bit RGB color with alpha
86#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
87#[cfg_attr(
88    feature = "serialization",
89    derive(serde_derive::Serialize, serde_derive::Deserialize)
90)]
91pub struct Color {
92    /// The red component of the color
93    pub r: u8,
94    /// The green component of the color
95    pub g: u8,
96    /// The blue component of the color
97    pub b: u8,
98    /// The opacity of the color
99    pub a: u8,
100}
101
102impl Color {
103    /// Returns a new Color from RGB values.
104    ///
105    /// # Parameters
106    /// * `r` - The color's amount of red.
107    /// * `g` - The color's amount of green.
108    /// * `b` - The color's amount of blue.
109    ///
110    /// # Example
111    /// ```
112    /// # use doryen_extra::color::Color;
113    /// let white = Color::new(255, 255, 255);
114    /// ```
115    pub const fn new(r: u8, g: u8, b: u8) -> Self {
116        Self { r, g, b, a: 255 }
117    }
118    /// Returns a new Color from RGB values with an opacity.
119    ///
120    /// # Parameters
121    /// * `r` - The color's amount of red.
122    /// * `g` - The color's amount of green.
123    /// * `b` - The color's amount of blue.
124    /// * `a` - The color's opacity.
125    ///
126    /// # Example
127    /// ```
128    /// # use doryen_extra::color::Color;
129    /// let translucent_white = Color::new_with_alpha(255, 255, 255, 127);
130    /// ```
131    pub const fn new_with_alpha(r: u8, g: u8, b: u8, a: u8) -> Self {
132        Self { r, g, b, a }
133    }
134
135    /// Returns a new Color from HSV values.
136    ///
137    /// The saturation and value parameters are automatically clamped to 0 and 1.
138    ///
139    /// Use `set_hsv()` to fill an existing struct with HSV values.
140    ///
141    /// # Parameters
142    /// * `hue` - The color's hue in degrees.
143    /// * `saturation` - The color's saturation, from 0 to 1.
144    /// * `value` - The color's value, from 0 to 1.
145    ///
146    /// # Example
147    /// ```
148    /// # use doryen_extra::color::Color;
149    /// let light_blue = Color::new_hsv(240.0, 0.75, 1.0);
150    /// ```
151    pub fn new_hsv(hue: f32, saturation: f32, value: f32) -> Self {
152        let mut color = Self::new(0, 0, 0);
153        color.set_hsv(hue, saturation, value);
154
155        color
156    }
157
158    /// Returns a new Color from HSV values with the given opacity.
159    ///
160    /// The saturation, value and opacity parameters are automatically clamped to 0 and 1.
161    ///
162    /// Use `set_hsv()` to fill an existing struct with HSV values.
163    ///
164    /// # Parameters
165    /// * `hue` - The color's hue in degrees.
166    /// * `saturation` - The color's saturation, from 0 to 1.
167    /// * `value` - The color's value, from 0 to 1.
168    /// * `opacity` - The color's opacity, from 0 to 1.
169    ///
170    /// # Example
171    /// ```
172    /// # use doryen_extra::color::Color;
173    /// let translucent_light_blue = Color::new_hsv_with_opacity(240.0, 0.75, 1.0, 0.5);
174    /// ```
175    pub fn new_hsv_with_opacity(hue: f32, saturation: f32, value: f32, opacity: f32) -> Self {
176        let a = (opacity.max(0.0).min(1.0) * 255.0).round() as u8;
177        let mut color = Self::new_with_alpha(0, 0, 0, a);
178        color.set_hsv(hue, saturation, value);
179
180        color
181    }
182
183    /// Sets a colors values from HSV values.
184    ///
185    /// # Parameters
186    /// * `hue` - The color's hue in degrees.
187    /// * `saturation` - The color's saturation, from 0 to 1.
188    /// * `value` - The color's value, from 0 to 1.
189    ///
190    /// Values outside the given ranges are clipped to fit within the allowed range.
191    #[allow(clippy::many_single_char_names)]
192    pub fn set_hsv(&mut self, hue: f32, saturation: f32, value: f32) {
193        let saturation = saturation.max(0.0).min(1.0);
194        let value = value.max(0.0).min(1.0);
195
196        if saturation == 0.0 {
197            /* achromatic (gray) */
198            let value = (value * 255.0).round() as u8;
199            self.r = value;
200            self.g = value;
201            self.b = value;
202            return;
203        }
204
205        let mut hue = hue.floor_modulo(360.0);
206        hue /= 60.0; /* sector 0 to 5 */
207        let hue_section = hue.floor() as i32;
208        let hue_fraction = hue - hue_section as f32;
209
210        let p = ((value * (1.0 - saturation)) * 255.0).round() as u8;
211        let q = ((value * (1.0 - saturation * hue_fraction)) * 255.0).round() as u8;
212        let t = ((value * (1.0 - saturation * (1.0 - hue_fraction))) * 255.0).round() as u8;
213        let v = (value * 255.0).round() as u8;
214
215        let (r, g, b) = match hue_section {
216            0 => (v, t, p),
217            1 => (q, v, p),
218            2 => (p, v, t),
219            3 => (p, q, v),
220            4 => (t, p, v),
221            _ => (v, p, q),
222        };
223        self.r = r;
224        self.g = g;
225        self.b = b;
226    }
227
228    /// Get a tuple of HSV values from a color.
229    pub fn get_hsv(self) -> (f32, f32, f32) {
230        let hue = self.get_hue();
231        let saturation = self.get_saturation();
232        let value = self.get_value();
233
234        (hue, saturation, value)
235    }
236
237    /// Change a color's hue.
238    ///
239    /// # Parameters
240    /// * `hue` - The color's hue in degrees. Values outside the
241    /// given range loop around to fit within the allowed range.
242    ///
243    /// # Examples
244    /// ```rust
245    /// # use doryen_extra::color::Color;
246    /// # let mut color = Color::CELADON;
247    /// // Sets the hue of the color to 16 degrees.
248    /// color.set_hue(16.);
249    /// # assert!((color.get_hue() - 16.).abs() < 1.);
250    /// ```
251    ///
252    /// ```rust
253    /// # use doryen_extra::color::Color;
254    /// # let mut color = Color::CELADON;
255    /// // Values outside the range of 0-360 will be clipped:
256    /// color.set_hue(420.);
257    ///
258    /// // The hue is actually set to 60 degrees.
259    /// let hue = color.get_hue();
260    /// assert!((hue - 60.).abs() < 1.);
261    ///
262    /// color.set_hue(-90.);
263    ///
264    /// // The hue is actually set to 270 degrees.
265    /// let hue = color.get_hue();
266    /// assert!((hue - 270.).abs() < 1.);
267    /// ```
268    pub fn set_hue(&mut self, hue: f32) {
269        let saturation = self.get_saturation();
270        let value = self.get_value();
271
272        self.set_hsv(hue, saturation, value);
273    }
274
275    /// Return a color's hue in degrees. See [`set_hue`] for examples.
276    ///
277    /// [`set_hue`]: #method.set_hue
278    pub fn get_hue(self) -> f32 {
279        let max = self.r.max(self.g).max(self.b);
280        let min = self.r.min(self.g).min(self.b);
281        let delta = f32::from(max) - f32::from(min);
282        if delta == 0.0 {
283            return 0.0;
284        }
285
286        let mut hue = if self.r == max {
287            (f32::from(self.g) - f32::from(self.b)) / delta
288        } else if self.g == max {
289            2.0 + (f32::from(self.b) - f32::from(self.r)) / delta
290        } else {
291            4.0 + (f32::from(self.r) - f32::from(self.g)) / delta
292        };
293        hue *= 60.0;
294        hue.floor_modulo(360.0)
295    }
296
297    /// Returns a color's saturation in the range \[0, 1\]. See [`set_saturation`] for examples.
298    ///
299    /// [`set_saturation`]: #method.set_saturation
300    pub fn get_saturation(self) -> f32 {
301        let max = self.r.max(self.g).max(self.b);
302        let min = self.r.min(self.g).min(self.b);
303        let delta = f32::from(max) - f32::from(min);
304        if max == 0 {
305            0.0
306        } else {
307            delta / f32::from(max)
308        }
309    }
310
311    /// Change a color's saturation.
312    ///
313    /// # Parameters
314    /// * `saturation` - The color's saturation, from 0 to 1. Values outside the
315    /// given range are clipped to fit within the allowed range.
316    ///
317    /// # Examples
318    /// ```rust
319    /// # use doryen_extra::color::Color;
320    /// # let mut color = Color::CELADON;
321    /// // Sets the saturation of the color to 0.75.
322    /// color.set_saturation(0.75);
323    /// # assert!((color.get_saturation() - 0.75).abs() < 0.001);
324    /// ```
325    ///
326    /// ```rust
327    /// # use doryen_extra::color::Color;
328    /// # let mut color = Color::CELADON;
329    /// // Values outside the range of 0-1 will be clipped:
330    /// color.set_saturation(2.);
331    ///
332    /// // The saturation is actually set to 1.
333    /// let saturation = color.get_saturation();
334    /// assert!((saturation - 1.).abs() < 0.001);
335    ///
336    /// color.set_saturation(-2.);
337    ///
338    /// // The saturation is actually set to 1.
339    /// let saturation = color.get_saturation();
340    /// assert!((saturation - 0.).abs() < 0.001);
341    /// ```
342    pub fn set_saturation(&mut self, saturation: f32) {
343        let hue = self.get_hue();
344        let value = self.get_value();
345
346        self.set_hsv(hue, saturation, value);
347    }
348
349    /// Returns a color's value in the range \[0, 1\].
350    pub fn get_value(self) -> f32 {
351        f32::from(self.r.max(self.g).max(self.b)) / 255.0
352    }
353
354    /// Change a color's value.
355    ///
356    /// # Parameters
357    /// * `value` - The color's value, from 0 to 1.
358    ///
359    /// # Examples
360    /// ```rust
361    /// # use doryen_extra::color::Color;
362    /// # let mut color = Color::CELADON;
363    /// // Sets the value of the color to 0.25.
364    /// color.set_value(0.25);
365    /// # assert!((color.get_value() - 0.25).abs() < 0.001);
366    /// ```
367    ///
368    /// ```rust
369    /// # use doryen_extra::color::Color;
370    /// # let mut color = Color::CELADON;
371    /// // Values outside the range of 0-1 will be clipped:
372    /// color.set_value(2.);
373    ///
374    /// // The saturation is actually set to 1.
375    /// let value = color.get_value();
376    /// assert!((value - 1.).abs() < 0.001);
377    ///
378    /// color.set_value(-2.);
379    ///
380    /// // The saturation is actually set to 0.
381    /// let value = color.get_value();
382    /// assert!((value - 0.).abs() < 0.001);
383    /// ```
384    pub fn set_value(&mut self, value: f32) {
385        let hue = self.get_hue();
386        let saturation = self.get_saturation();
387
388        self.set_hsv(hue, saturation, value);
389    }
390
391    /// Shift a color's hue by an amount.
392    ///
393    /// # Parameters
394    /// * `hue_shift` - The distance to shift the hue, in degrees.
395    ///
396    /// # Example
397    ///
398    /// ```rust
399    /// # use doryen_extra::color::Color;
400    /// # let mut color = Color::CELADON;
401    /// // Shift the hue by 10 degrees
402    /// color.shift_hue(10.);
403    /// # assert!(color.get_hue() - (Color::CELADON.get_hue() + 10.).abs() < 1.);
404    ///
405    /// // Shift the hue back to what it was originally
406    /// color.shift_hue(-10.);
407    /// # assert!((color.get_hue() - Color::CELADON.get_hue()).abs() < 1.);
408    /// ```
409    pub fn shift_hue(&mut self, hue_shift: f32) {
410        if hue_shift == 0.0 {
411            return;
412        }
413        self.set_hsv(
414            self.get_hue() + hue_shift,
415            self.get_saturation(),
416            self.get_value(),
417        );
418    }
419
420    /// Scale a color's saturation and value.
421    ///
422    /// # Parameters
423    /// * `saturation_coefficient` - Multiplier for this color's saturation.
424    /// * `value_coefficient` - Multiplier for this color's value.
425    ///
426    /// # Example
427    ///
428    /// ```rust
429    /// # use doryen_extra::color::Color;
430    ///
431    /// // Let's make a color with a saturation of 0.5 and a value of 0.75
432    /// let mut color = Color::new_hsv(10., 0.5, 0.75);
433    ///
434    /// // Scale them by 1/2 and 1/3, respectively
435    /// color.scale_hsv(0.5, 1. / 3.);
436    ///
437    /// // They should both be 0.25 now
438    /// assert!((color.get_saturation() - 0.25).abs() < 0.001);
439    /// assert!((color.get_value() - 0.25).abs() < 0.001);
440    /// ```
441    pub fn scale_hsv(&mut self, saturation_coefficient: f32, value_coefficient: f32) {
442        if (saturation_coefficient - 1.0).abs() < 0.001 && (value_coefficient - 1.0).abs() < 0.001 {
443            return;
444        }
445        self.set_hsv(
446            self.get_hue(),
447            self.get_saturation() * saturation_coefficient,
448            self.get_value() * value_coefficient,
449        );
450    }
451
452    /// Generates an interpolated gradient of colors using RGB interpolation.
453    ///
454    /// Using RGB interpolation between colors is almost always the wrong choice and tends to
455    /// produce really ugly results. You almost certainly don't want to use this; use
456    /// `generate_gradient_hsv()` instead.
457    ///
458    /// # Parameters
459    /// * `key_colors` -  The colors to make gradients between.
460    /// * `gradient_spans` -  How many interpolated colors to generate between each
461    /// pair of key colors.
462    ///
463    /// # Panics
464    /// * If `gradient_spans`' length isn't one less than `key_colors`' length.
465    ///
466    /// # Example
467    /// ```
468    /// # use doryen_extra::color::Color;
469    /// // Generates no colors at all
470    /// let none = Color::generate_gradient_rgb(&[], &[]);
471    ///
472    /// assert!(none.is_empty());
473    /// ```
474    ///
475    /// ```
476    /// # use doryen_extra::color::Color;
477    /// // Generates only the given color
478    /// let one = Color::generate_gradient_rgb(&[Color::WHITE], &[]);
479    ///
480    /// assert_eq!(one.len(), 1);
481    /// assert_eq!(one[0], Color::WHITE);
482    /// ```
483    ///
484    ///
485    /// ```
486    /// # use doryen_extra::color::Color;
487    /// // Generates every grayscale color between black and white
488    /// let grayscale = Color::generate_gradient_rgb(&[Color::BLACK, Color::WHITE], &[254]);
489    ///
490    /// assert_eq!(grayscale.len(), 256);
491    /// # for (i, color) in grayscale.iter().enumerate() {
492    /// #     assert_eq!(color.r, i as u8);
493    /// #     assert_eq!(color.g, i as u8);
494    /// #     assert_eq!(color.b, i as u8);
495    /// # }
496    /// ```
497    pub fn generate_gradient_rgb(key_colors: &[Self], gradient_spans: &[usize]) -> Vec<Self> {
498        if key_colors.is_empty() {
499            return vec![];
500        }
501
502        assert_eq!(
503            key_colors.len() - 1,
504            gradient_spans.len(),
505            "gradient_spans should have one fewer values in it than key_colors"
506        );
507
508        let mut result =
509            Vec::with_capacity(key_colors.len() + gradient_spans.iter().sum::<usize>());
510        for (span, colors) in key_colors.windows(2).enumerate() {
511            let start_color = colors[0];
512            let end_color = colors[1];
513            for s in 0..=gradient_spans[span] {
514                let coefficient = s as f32 / (gradient_spans[span] + 1) as f32;
515                result.push(start_color.lerp_rgb(end_color, coefficient));
516            }
517        }
518        result.push(*key_colors.last().unwrap());
519
520        result
521    }
522
523    /// Generates an interpolated gradient of colors using HSV interpolation.
524    ///
525    /// # Parameters
526    /// * `key_colors` -  The colors to make gradients between.
527    /// * `gradient_spans` -  How many interpolated colors to generate between each
528    /// pair of key colors.
529    ///
530    /// # Panics
531    /// * If `gradient_spans`' length isn't one less than `key_colors`' length.
532    ///
533    /// # Examples
534    /// ```
535    /// # use doryen_extra::color::Color;
536    /// // Generates no colors at all
537    /// let none = Color::generate_gradient_hsv(&[], &[]);
538    ///
539    /// assert!(none.is_empty());
540    /// ```
541    ///
542    /// ```
543    /// # use doryen_extra::color::Color;
544    /// // Generates only the given color
545    /// let one = Color::generate_gradient_hsv(&[Color::WHITE], &[]);
546    ///
547    /// assert_eq!(one.len(), 1);
548    /// assert_eq!(one[0], Color::WHITE);
549    /// ```
550    ///
551    /// ```
552    /// # use doryen_extra::color::Color;
553    /// // Generates every grayscale color between black and white
554    /// let grayscale = Color::generate_gradient_hsv(&[Color::BLACK, Color::WHITE], &[254]);
555    /// ```
556    pub fn generate_gradient_hsv(key_colors: &[Self], gradient_spans: &[usize]) -> Vec<Self> {
557        if key_colors.is_empty() {
558            return vec![];
559        }
560
561        assert_eq!(
562            key_colors.len() - 1,
563            gradient_spans.len(),
564            "gradient_spans should have one fewer values in it than key_colors"
565        );
566
567        let mut result =
568            Vec::with_capacity(key_colors.len() + gradient_spans.iter().sum::<usize>());
569        for (span, colors) in key_colors.windows(2).enumerate() {
570            let start_color = colors[0];
571            let end_color = colors[1];
572            for s in 0..=gradient_spans[span] {
573                let coefficient = s as f32 / (gradient_spans[span] + 1) as f32;
574                result.push(start_color.lerp_hsv(end_color, coefficient));
575            }
576        }
577        result.push(*key_colors.last().unwrap());
578
579        result
580    }
581
582    /// Interpolate two colors together using their RGB representation and return the result.
583    ///
584    /// You almost certainly don't want to use this; use `lerp_hsv()` instead.
585    ///
586    /// # Parameters
587    /// * `other` - The second color.
588    /// * `coefficient` - The coefficient. 0 for entirely the first color, 1 for entirely the second.
589    ///
590    /// # Panics
591    ///
592    /// If `coefficient` is outside the range \[0, 1\].
593    pub fn lerp_rgb(self, other: Self, coefficient: f32) -> Self {
594        assert!(
595            coefficient >= 0.0 && coefficient <= 1.0,
596            "coefficient is outside the acceptable range [0, 1]"
597        );
598
599        Self::new_with_alpha(
600            (f32::from(self.r) + (f32::from(other.r) - f32::from(self.r)) * coefficient) as u8,
601            (f32::from(self.g) + (f32::from(other.g) - f32::from(self.g)) * coefficient) as u8,
602            (f32::from(self.b) + (f32::from(other.b) - f32::from(self.b)) * coefficient) as u8,
603            (f32::from(self.a) + (f32::from(other.a) - f32::from(self.a)) * coefficient) as u8,
604        )
605    }
606
607    /// Interpolate two colors together using their HSV representation and return the result.
608    ///
609    /// # Parameters
610    /// * `other` - The second color.
611    /// * `coefficient` - The coefficient. 0 for entirely the first color, 1 for entirely the second.
612    ///
613    /// # Panics
614    ///
615    /// If `coefficient` is outside the range \[0, 1\].
616    pub fn lerp_hsv(self, other: Self, coefficient: f32) -> Self {
617        assert!(
618            coefficient >= 0.0 && coefficient <= 1.0,
619            "coefficient is outside the acceptable range [0, 1]"
620        );
621        let (self_hue, self_saturation, self_value) = self.get_hsv();
622        let (other_hue, other_saturation, other_value) = other.get_hsv();
623
624        let hue_diff = other_hue - self_hue;
625        let hue_delta = hue_diff
626            + if hue_diff.abs() > 180.0 {
627                if hue_diff < 0.0 {
628                    360.0
629                } else {
630                    -360.0
631                }
632            } else {
633                0.0
634            };
635
636        let hue_interpolated = self_hue + coefficient * hue_delta;
637
638        let opacity_interpolated =
639            (f32::from(self.a) + (f32::from(other.a) - f32::from(self.a)) * coefficient) / 255.0;
640
641        Self::new_hsv_with_opacity(
642            hue_interpolated,
643            self_saturation + (other_saturation - self_saturation) * coefficient,
644            self_value + (other_value - self_value) * coefficient,
645            opacity_interpolated,
646        )
647    }
648}
649
650// Enums-to-color
651impl Color {
652    /// Takes a `Name` and `Level` value and returns the corresponding color constant.
653    #[allow(clippy::too_many_lines)]
654    pub fn by_name_and_level(name: Name, level: Level) -> Self {
655        match name {
656            Name::Red => match level {
657                Level::Desaturated => Self::DESATURATED_RED,
658                Level::Lightest => Self::LIGHTEST_RED,
659                Level::Lighter => Self::LIGHTER_RED,
660                Level::Light => Self::LIGHT_RED,
661                Level::Normal => Self::RED,
662                Level::Dark => Self::DARK_RED,
663                Level::Darker => Self::DARKER_RED,
664                Level::Darkest => Self::DARKEST_RED,
665            },
666            Name::Flame => match level {
667                Level::Desaturated => Self::DESATURATED_FLAME,
668                Level::Lightest => Self::LIGHTEST_FLAME,
669                Level::Lighter => Self::LIGHTER_FLAME,
670                Level::Light => Self::LIGHT_FLAME,
671                Level::Normal => Self::FLAME,
672                Level::Dark => Self::DARK_FLAME,
673                Level::Darker => Self::DARKER_FLAME,
674                Level::Darkest => Self::DARKEST_FLAME,
675            },
676            Name::Orange => match level {
677                Level::Desaturated => Self::DESATURATED_ORANGE,
678                Level::Lightest => Self::LIGHTEST_ORANGE,
679                Level::Lighter => Self::LIGHTER_ORANGE,
680                Level::Light => Self::LIGHT_ORANGE,
681                Level::Normal => Self::ORANGE,
682                Level::Dark => Self::DARK_ORANGE,
683                Level::Darker => Self::DARKER_ORANGE,
684                Level::Darkest => Self::DARKEST_ORANGE,
685            },
686            Name::Amber => match level {
687                Level::Desaturated => Self::DESATURATED_AMBER,
688                Level::Lightest => Self::LIGHTEST_AMBER,
689                Level::Lighter => Self::LIGHTER_AMBER,
690                Level::Light => Self::LIGHT_AMBER,
691                Level::Normal => Self::AMBER,
692                Level::Dark => Self::DARK_AMBER,
693                Level::Darker => Self::DARKER_AMBER,
694                Level::Darkest => Self::DARKEST_AMBER,
695            },
696            Name::Yellow => match level {
697                Level::Desaturated => Self::DESATURATED_YELLOW,
698                Level::Lightest => Self::LIGHTEST_YELLOW,
699                Level::Lighter => Self::LIGHTER_YELLOW,
700                Level::Light => Self::LIGHT_YELLOW,
701                Level::Normal => Self::YELLOW,
702                Level::Dark => Self::DARK_YELLOW,
703                Level::Darker => Self::DARKER_YELLOW,
704                Level::Darkest => Self::DARKEST_YELLOW,
705            },
706            Name::Lime => match level {
707                Level::Desaturated => Self::DESATURATED_LIME,
708                Level::Lightest => Self::LIGHTEST_LIME,
709                Level::Lighter => Self::LIGHTER_LIME,
710                Level::Light => Self::LIGHT_LIME,
711                Level::Normal => Self::LIME,
712                Level::Dark => Self::DARK_LIME,
713                Level::Darker => Self::DARKER_LIME,
714                Level::Darkest => Self::DARKEST_LIME,
715            },
716            Name::Chartreuse => match level {
717                Level::Desaturated => Self::DESATURATED_CHARTREUSE,
718                Level::Lightest => Self::LIGHTEST_CHARTREUSE,
719                Level::Lighter => Self::LIGHTER_CHARTREUSE,
720                Level::Light => Self::LIGHT_CHARTREUSE,
721                Level::Normal => Self::CHARTREUSE,
722                Level::Dark => Self::DARK_CHARTREUSE,
723                Level::Darker => Self::DARKER_CHARTREUSE,
724                Level::Darkest => Self::DARKEST_CHARTREUSE,
725            },
726            Name::Green => match level {
727                Level::Desaturated => Self::DESATURATED_GREEN,
728                Level::Lightest => Self::LIGHTEST_GREEN,
729                Level::Lighter => Self::LIGHTER_GREEN,
730                Level::Light => Self::LIGHT_GREEN,
731                Level::Normal => Self::GREEN,
732                Level::Dark => Self::DARK_GREEN,
733                Level::Darker => Self::DARKER_GREEN,
734                Level::Darkest => Self::DARKEST_GREEN,
735            },
736            Name::Sea => match level {
737                Level::Desaturated => Self::DESATURATED_SEA,
738                Level::Lightest => Self::LIGHTEST_SEA,
739                Level::Lighter => Self::LIGHTER_SEA,
740                Level::Light => Self::LIGHT_SEA,
741                Level::Normal => Self::SEA,
742                Level::Dark => Self::DARK_SEA,
743                Level::Darker => Self::DARKER_SEA,
744                Level::Darkest => Self::DARKEST_SEA,
745            },
746            Name::Turquoise => match level {
747                Level::Desaturated => Self::DESATURATED_TURQUOISE,
748                Level::Lightest => Self::LIGHTEST_TURQUOISE,
749                Level::Lighter => Self::LIGHTER_TURQUOISE,
750                Level::Light => Self::LIGHT_TURQUOISE,
751                Level::Normal => Self::TURQUOISE,
752                Level::Dark => Self::DARK_TURQUOISE,
753                Level::Darker => Self::DARKER_TURQUOISE,
754                Level::Darkest => Self::DARKEST_TURQUOISE,
755            },
756            Name::Cyan => match level {
757                Level::Desaturated => Self::DESATURATED_CYAN,
758                Level::Lightest => Self::LIGHTEST_CYAN,
759                Level::Lighter => Self::LIGHTER_CYAN,
760                Level::Light => Self::LIGHT_CYAN,
761                Level::Normal => Self::CYAN,
762                Level::Dark => Self::DARK_CYAN,
763                Level::Darker => Self::DARKER_CYAN,
764                Level::Darkest => Self::DARKEST_CYAN,
765            },
766            Name::Sky => match level {
767                Level::Desaturated => Self::DESATURATED_SKY,
768                Level::Lightest => Self::LIGHTEST_SKY,
769                Level::Lighter => Self::LIGHTER_SKY,
770                Level::Light => Self::LIGHT_SKY,
771                Level::Normal => Self::SKY,
772                Level::Dark => Self::DARK_SKY,
773                Level::Darker => Self::DARKER_SKY,
774                Level::Darkest => Self::DARKEST_SKY,
775            },
776            Name::Azure => match level {
777                Level::Desaturated => Self::DESATURATED_AZURE,
778                Level::Lightest => Self::LIGHTEST_AZURE,
779                Level::Lighter => Self::LIGHTER_AZURE,
780                Level::Light => Self::LIGHT_AZURE,
781                Level::Normal => Self::AZURE,
782                Level::Dark => Self::DARK_AZURE,
783                Level::Darker => Self::DARKER_AZURE,
784                Level::Darkest => Self::DARKEST_AZURE,
785            },
786            Name::Blue => match level {
787                Level::Desaturated => Self::DESATURATED_BLUE,
788                Level::Lightest => Self::LIGHTEST_BLUE,
789                Level::Lighter => Self::LIGHTER_BLUE,
790                Level::Light => Self::LIGHT_BLUE,
791                Level::Normal => Self::BLUE,
792                Level::Dark => Self::DARK_BLUE,
793                Level::Darker => Self::DARKER_BLUE,
794                Level::Darkest => Self::DARKEST_BLUE,
795            },
796            Name::Han => match level {
797                Level::Desaturated => Self::DESATURATED_HAN,
798                Level::Lightest => Self::LIGHTEST_HAN,
799                Level::Lighter => Self::LIGHTER_HAN,
800                Level::Light => Self::LIGHT_HAN,
801                Level::Normal => Self::HAN,
802                Level::Dark => Self::DARK_HAN,
803                Level::Darker => Self::DARKER_HAN,
804                Level::Darkest => Self::DARKEST_HAN,
805            },
806            Name::Violet => match level {
807                Level::Desaturated => Self::DESATURATED_VIOLET,
808                Level::Lightest => Self::LIGHTEST_VIOLET,
809                Level::Lighter => Self::LIGHTER_VIOLET,
810                Level::Light => Self::LIGHT_VIOLET,
811                Level::Normal => Self::VIOLET,
812                Level::Dark => Self::DARK_VIOLET,
813                Level::Darker => Self::DARKER_VIOLET,
814                Level::Darkest => Self::DARKEST_VIOLET,
815            },
816            Name::Purple => match level {
817                Level::Desaturated => Self::DESATURATED_PURPLE,
818                Level::Lightest => Self::LIGHTEST_PURPLE,
819                Level::Lighter => Self::LIGHTER_PURPLE,
820                Level::Light => Self::LIGHT_PURPLE,
821                Level::Normal => Self::PURPLE,
822                Level::Dark => Self::DARK_PURPLE,
823                Level::Darker => Self::DARKER_PURPLE,
824                Level::Darkest => Self::DARKEST_PURPLE,
825            },
826            Name::Fuchsia => match level {
827                Level::Desaturated => Self::DESATURATED_FUCHSIA,
828                Level::Lightest => Self::LIGHTEST_FUCHSIA,
829                Level::Lighter => Self::LIGHTER_FUCHSIA,
830                Level::Light => Self::LIGHT_FUCHSIA,
831                Level::Normal => Self::FUCHSIA,
832                Level::Dark => Self::DARK_FUCHSIA,
833                Level::Darker => Self::DARKER_FUCHSIA,
834                Level::Darkest => Self::DARKEST_FUCHSIA,
835            },
836            Name::Magenta => match level {
837                Level::Desaturated => Self::DESATURATED_MAGENTA,
838                Level::Lightest => Self::LIGHTEST_MAGENTA,
839                Level::Lighter => Self::LIGHTER_MAGENTA,
840                Level::Light => Self::LIGHT_MAGENTA,
841                Level::Normal => Self::MAGENTA,
842                Level::Dark => Self::DARK_MAGENTA,
843                Level::Darker => Self::DARKER_MAGENTA,
844                Level::Darkest => Self::DARKEST_MAGENTA,
845            },
846            Name::Pink => match level {
847                Level::Desaturated => Self::DESATURATED_PINK,
848                Level::Lightest => Self::LIGHTEST_PINK,
849                Level::Lighter => Self::LIGHTER_PINK,
850                Level::Light => Self::LIGHT_PINK,
851                Level::Normal => Self::PINK,
852                Level::Dark => Self::DARK_PINK,
853                Level::Darker => Self::DARKER_PINK,
854                Level::Darkest => Self::DARKEST_PINK,
855            },
856            Name::Crimson => match level {
857                Level::Desaturated => Self::DESATURATED_CRIMSON,
858                Level::Lightest => Self::LIGHTEST_CRIMSON,
859                Level::Lighter => Self::LIGHTER_CRIMSON,
860                Level::Light => Self::LIGHT_CRIMSON,
861                Level::Normal => Self::CRIMSON,
862                Level::Dark => Self::DARK_CRIMSON,
863                Level::Darker => Self::DARKER_CRIMSON,
864                Level::Darkest => Self::DARKEST_CRIMSON,
865            },
866        }
867    }
868}
869
870// Constants
871#[allow(missing_docs)]
872impl Color {
873    /* color values */
874    pub const BLACK: Self = Self::new(0, 0, 0);
875    pub const DARKEST_GRAY: Self = Self::new(31, 31, 31);
876    pub const DARKER_GRAY: Self = Self::new(63, 63, 63);
877    pub const DARK_GRAY: Self = Self::new(95, 95, 95);
878    pub const GRAY: Self = Self::new(127, 127, 127);
879    pub const LIGHT_GRAY: Self = Self::new(159, 159, 159);
880    pub const LIGHTER_GRAY: Self = Self::new(191, 191, 191);
881    pub const LIGHTEST_GRAY: Self = Self::new(223, 223, 223);
882    pub const DARKEST_GREY: Self = Self::DARKEST_GRAY;
883    pub const DARKER_GREY: Self = Self::DARKER_GRAY;
884    pub const DARK_GREY: Self = Self::DARK_GRAY;
885    pub const GREY: Self = Self::GRAY;
886    pub const LIGHT_GREY: Self = Self::LIGHT_GRAY;
887    pub const LIGHTER_GREY: Self = Self::LIGHTER_GRAY;
888    pub const LIGHTEST_GREY: Self = Self::LIGHTEST_GRAY;
889    pub const WHITE: Self = Self::new(255, 255, 255);
890
891    pub const DARKEST_SEPIA: Self = Self::new(31, 24, 15);
892    pub const DARKER_SEPIA: Self = Self::new(63, 50, 31);
893    pub const DARK_SEPIA: Self = Self::new(94, 75, 47);
894    pub const SEPIA: Self = Self::new(127, 101, 63);
895    pub const LIGHT_SEPIA: Self = Self::new(158, 134, 100);
896    pub const LIGHTER_SEPIA: Self = Self::new(191, 171, 143);
897    pub const LIGHTEST_SEPIA: Self = Self::new(222, 211, 195);
898
899    /* desaturated */
900    pub const DESATURATED_RED: Self = Self::new(127, 63, 63);
901    pub const DESATURATED_FLAME: Self = Self::new(127, 79, 63);
902    pub const DESATURATED_ORANGE: Self = Self::new(127, 95, 63);
903    pub const DESATURATED_AMBER: Self = Self::new(127, 111, 63);
904    pub const DESATURATED_YELLOW: Self = Self::new(127, 127, 63);
905    pub const DESATURATED_LIME: Self = Self::new(111, 127, 63);
906    pub const DESATURATED_CHARTREUSE: Self = Self::new(95, 127, 63);
907    pub const DESATURATED_GREEN: Self = Self::new(63, 127, 63);
908    pub const DESATURATED_SEA: Self = Self::new(63, 127, 95);
909    pub const DESATURATED_TURQUOISE: Self = Self::new(63, 127, 111);
910    pub const DESATURATED_CYAN: Self = Self::new(63, 127, 127);
911    pub const DESATURATED_SKY: Self = Self::new(63, 111, 127);
912    pub const DESATURATED_AZURE: Self = Self::new(63, 95, 127);
913    pub const DESATURATED_BLUE: Self = Self::new(63, 63, 127);
914    pub const DESATURATED_HAN: Self = Self::new(79, 63, 127);
915    pub const DESATURATED_VIOLET: Self = Self::new(95, 63, 127);
916    pub const DESATURATED_PURPLE: Self = Self::new(111, 63, 127);
917    pub const DESATURATED_FUCHSIA: Self = Self::new(127, 63, 127);
918    pub const DESATURATED_MAGENTA: Self = Self::new(127, 63, 111);
919    pub const DESATURATED_PINK: Self = Self::new(127, 63, 95);
920    pub const DESATURATED_CRIMSON: Self = Self::new(127, 63, 79);
921
922    /* lightest */
923    pub const LIGHTEST_RED: Self = Self::new(255, 191, 191);
924    pub const LIGHTEST_FLAME: Self = Self::new(255, 207, 191);
925    pub const LIGHTEST_ORANGE: Self = Self::new(255, 223, 191);
926    pub const LIGHTEST_AMBER: Self = Self::new(255, 239, 191);
927    pub const LIGHTEST_YELLOW: Self = Self::new(255, 255, 191);
928    pub const LIGHTEST_LIME: Self = Self::new(239, 255, 191);
929    pub const LIGHTEST_CHARTREUSE: Self = Self::new(223, 255, 191);
930    pub const LIGHTEST_GREEN: Self = Self::new(191, 255, 191);
931    pub const LIGHTEST_SEA: Self = Self::new(191, 255, 223);
932    pub const LIGHTEST_TURQUOISE: Self = Self::new(191, 255, 239);
933    pub const LIGHTEST_CYAN: Self = Self::new(191, 255, 255);
934    pub const LIGHTEST_SKY: Self = Self::new(191, 239, 255);
935    pub const LIGHTEST_AZURE: Self = Self::new(191, 223, 255);
936    pub const LIGHTEST_BLUE: Self = Self::new(191, 191, 255);
937    pub const LIGHTEST_HAN: Self = Self::new(207, 191, 255);
938    pub const LIGHTEST_VIOLET: Self = Self::new(223, 191, 255);
939    pub const LIGHTEST_PURPLE: Self = Self::new(239, 191, 255);
940    pub const LIGHTEST_FUCHSIA: Self = Self::new(255, 191, 255);
941    pub const LIGHTEST_MAGENTA: Self = Self::new(255, 191, 239);
942    pub const LIGHTEST_PINK: Self = Self::new(255, 191, 223);
943    pub const LIGHTEST_CRIMSON: Self = Self::new(255, 191, 207);
944
945    /* lighter */
946    pub const LIGHTER_RED: Self = Self::new(255, 127, 127);
947    pub const LIGHTER_FLAME: Self = Self::new(255, 159, 127);
948    pub const LIGHTER_ORANGE: Self = Self::new(255, 191, 127);
949    pub const LIGHTER_AMBER: Self = Self::new(255, 223, 127);
950    pub const LIGHTER_YELLOW: Self = Self::new(255, 255, 127);
951    pub const LIGHTER_LIME: Self = Self::new(223, 255, 127);
952    pub const LIGHTER_CHARTREUSE: Self = Self::new(191, 255, 127);
953    pub const LIGHTER_GREEN: Self = Self::new(127, 255, 127);
954    pub const LIGHTER_SEA: Self = Self::new(127, 255, 191);
955    pub const LIGHTER_TURQUOISE: Self = Self::new(127, 255, 223);
956    pub const LIGHTER_CYAN: Self = Self::new(127, 255, 255);
957    pub const LIGHTER_SKY: Self = Self::new(127, 223, 255);
958    pub const LIGHTER_AZURE: Self = Self::new(127, 191, 255);
959    pub const LIGHTER_BLUE: Self = Self::new(127, 127, 255);
960    pub const LIGHTER_HAN: Self = Self::new(159, 127, 255);
961    pub const LIGHTER_VIOLET: Self = Self::new(191, 127, 255);
962    pub const LIGHTER_PURPLE: Self = Self::new(223, 127, 255);
963    pub const LIGHTER_FUCHSIA: Self = Self::new(255, 127, 255);
964    pub const LIGHTER_MAGENTA: Self = Self::new(255, 127, 223);
965    pub const LIGHTER_PINK: Self = Self::new(255, 127, 191);
966    pub const LIGHTER_CRIMSON: Self = Self::new(255, 127, 159);
967
968    /* light */
969    pub const LIGHT_RED: Self = Self::new(255, 63, 63);
970    pub const LIGHT_FLAME: Self = Self::new(255, 111, 63);
971    pub const LIGHT_ORANGE: Self = Self::new(255, 159, 63);
972    pub const LIGHT_AMBER: Self = Self::new(255, 207, 63);
973    pub const LIGHT_YELLOW: Self = Self::new(255, 255, 63);
974    pub const LIGHT_LIME: Self = Self::new(207, 255, 63);
975    pub const LIGHT_CHARTREUSE: Self = Self::new(159, 255, 63);
976    pub const LIGHT_GREEN: Self = Self::new(63, 255, 63);
977    pub const LIGHT_SEA: Self = Self::new(63, 255, 159);
978    pub const LIGHT_TURQUOISE: Self = Self::new(63, 255, 207);
979    pub const LIGHT_CYAN: Self = Self::new(63, 255, 255);
980    pub const LIGHT_SKY: Self = Self::new(63, 207, 255);
981    pub const LIGHT_AZURE: Self = Self::new(63, 159, 255);
982    pub const LIGHT_BLUE: Self = Self::new(63, 63, 255);
983    pub const LIGHT_HAN: Self = Self::new(111, 63, 255);
984    pub const LIGHT_VIOLET: Self = Self::new(159, 63, 255);
985    pub const LIGHT_PURPLE: Self = Self::new(207, 63, 255);
986    pub const LIGHT_FUCHSIA: Self = Self::new(255, 63, 255);
987    pub const LIGHT_MAGENTA: Self = Self::new(255, 63, 207);
988    pub const LIGHT_PINK: Self = Self::new(255, 63, 159);
989    pub const LIGHT_CRIMSON: Self = Self::new(255, 63, 111);
990
991    /* normal */
992    pub const RED: Self = Self::new(255, 0, 0);
993    pub const FLAME: Self = Self::new(255, 63, 0);
994    pub const ORANGE: Self = Self::new(255, 127, 0);
995    pub const AMBER: Self = Self::new(255, 191, 0);
996    pub const YELLOW: Self = Self::new(255, 255, 0);
997    pub const LIME: Self = Self::new(191, 255, 0);
998    pub const CHARTREUSE: Self = Self::new(127, 255, 0);
999    pub const GREEN: Self = Self::new(0, 255, 0);
1000    pub const SEA: Self = Self::new(0, 255, 127);
1001    pub const TURQUOISE: Self = Self::new(0, 255, 191);
1002    pub const CYAN: Self = Self::new(0, 255, 255);
1003    pub const SKY: Self = Self::new(0, 191, 255);
1004    pub const AZURE: Self = Self::new(0, 127, 255);
1005    pub const BLUE: Self = Self::new(0, 0, 255);
1006    pub const HAN: Self = Self::new(63, 0, 255);
1007    pub const VIOLET: Self = Self::new(127, 0, 255);
1008    pub const PURPLE: Self = Self::new(191, 0, 255);
1009    pub const FUCHSIA: Self = Self::new(255, 0, 255);
1010    pub const MAGENTA: Self = Self::new(255, 0, 191);
1011    pub const PINK: Self = Self::new(255, 0, 127);
1012    pub const CRIMSON: Self = Self::new(255, 0, 63);
1013
1014    /* dark */
1015    pub const DARK_RED: Self = Self::new(191, 0, 0);
1016    pub const DARK_FLAME: Self = Self::new(191, 47, 0);
1017    pub const DARK_ORANGE: Self = Self::new(191, 95, 0);
1018    pub const DARK_AMBER: Self = Self::new(191, 143, 0);
1019    pub const DARK_YELLOW: Self = Self::new(191, 191, 0);
1020    pub const DARK_LIME: Self = Self::new(143, 191, 0);
1021    pub const DARK_CHARTREUSE: Self = Self::new(95, 191, 0);
1022    pub const DARK_GREEN: Self = Self::new(0, 191, 0);
1023    pub const DARK_SEA: Self = Self::new(0, 191, 95);
1024    pub const DARK_TURQUOISE: Self = Self::new(0, 191, 143);
1025    pub const DARK_CYAN: Self = Self::new(0, 191, 191);
1026    pub const DARK_SKY: Self = Self::new(0, 143, 191);
1027    pub const DARK_AZURE: Self = Self::new(0, 95, 191);
1028    pub const DARK_BLUE: Self = Self::new(0, 0, 191);
1029    pub const DARK_HAN: Self = Self::new(47, 0, 191);
1030    pub const DARK_VIOLET: Self = Self::new(95, 0, 191);
1031    pub const DARK_PURPLE: Self = Self::new(143, 0, 191);
1032    pub const DARK_FUCHSIA: Self = Self::new(191, 0, 191);
1033    pub const DARK_MAGENTA: Self = Self::new(191, 0, 143);
1034    pub const DARK_PINK: Self = Self::new(191, 0, 95);
1035    pub const DARK_CRIMSON: Self = Self::new(191, 0, 47);
1036
1037    /* darker */
1038    pub const DARKER_RED: Self = Self::new(127, 0, 0);
1039    pub const DARKER_FLAME: Self = Self::new(127, 31, 0);
1040    pub const DARKER_ORANGE: Self = Self::new(127, 63, 0);
1041    pub const DARKER_AMBER: Self = Self::new(127, 95, 0);
1042    pub const DARKER_YELLOW: Self = Self::new(127, 127, 0);
1043    pub const DARKER_LIME: Self = Self::new(95, 127, 0);
1044    pub const DARKER_CHARTREUSE: Self = Self::new(63, 127, 0);
1045    pub const DARKER_GREEN: Self = Self::new(0, 127, 0);
1046    pub const DARKER_SEA: Self = Self::new(0, 127, 63);
1047    pub const DARKER_TURQUOISE: Self = Self::new(0, 127, 95);
1048    pub const DARKER_CYAN: Self = Self::new(0, 127, 127);
1049    pub const DARKER_SKY: Self = Self::new(0, 95, 127);
1050    pub const DARKER_AZURE: Self = Self::new(0, 63, 127);
1051    pub const DARKER_BLUE: Self = Self::new(0, 0, 127);
1052    pub const DARKER_HAN: Self = Self::new(31, 0, 127);
1053    pub const DARKER_VIOLET: Self = Self::new(63, 0, 127);
1054    pub const DARKER_PURPLE: Self = Self::new(95, 0, 127);
1055    pub const DARKER_FUCHSIA: Self = Self::new(127, 0, 127);
1056    pub const DARKER_MAGENTA: Self = Self::new(127, 0, 95);
1057    pub const DARKER_PINK: Self = Self::new(127, 0, 63);
1058    pub const DARKER_CRIMSON: Self = Self::new(127, 0, 31);
1059
1060    /* darkest */
1061    pub const DARKEST_RED: Self = Self::new(63, 0, 0);
1062    pub const DARKEST_FLAME: Self = Self::new(63, 15, 0);
1063    pub const DARKEST_ORANGE: Self = Self::new(63, 31, 0);
1064    pub const DARKEST_AMBER: Self = Self::new(63, 47, 0);
1065    pub const DARKEST_YELLOW: Self = Self::new(63, 63, 0);
1066    pub const DARKEST_LIME: Self = Self::new(47, 63, 0);
1067    pub const DARKEST_CHARTREUSE: Self = Self::new(31, 63, 0);
1068    pub const DARKEST_GREEN: Self = Self::new(0, 63, 0);
1069    pub const DARKEST_SEA: Self = Self::new(0, 63, 31);
1070    pub const DARKEST_TURQUOISE: Self = Self::new(0, 63, 47);
1071    pub const DARKEST_CYAN: Self = Self::new(0, 63, 63);
1072    pub const DARKEST_SKY: Self = Self::new(0, 47, 63);
1073    pub const DARKEST_AZURE: Self = Self::new(0, 31, 63);
1074    pub const DARKEST_BLUE: Self = Self::new(0, 0, 63);
1075    pub const DARKEST_HAN: Self = Self::new(15, 0, 63);
1076    pub const DARKEST_VIOLET: Self = Self::new(31, 0, 63);
1077    pub const DARKEST_PURPLE: Self = Self::new(47, 0, 63);
1078    pub const DARKEST_FUCHSIA: Self = Self::new(63, 0, 63);
1079    pub const DARKEST_MAGENTA: Self = Self::new(63, 0, 47);
1080    pub const DARKEST_PINK: Self = Self::new(63, 0, 31);
1081    pub const DARKEST_CRIMSON: Self = Self::new(63, 0, 15);
1082
1083    /* metallic */
1084    pub const BRASS: Self = Self::new(191, 151, 96);
1085    pub const COPPER: Self = Self::new(197, 136, 124);
1086    pub const GOLD: Self = Self::new(229, 191, 0);
1087    pub const SILVER: Self = Self::new(203, 203, 203);
1088
1089    /* miscellaneous */
1090    pub const CELADON: Self = Self::new(172, 255, 175);
1091    pub const PEACH: Self = Self::new(255, 159, 127);
1092}
1093
1094impl Add for Color {
1095    type Output = Self;
1096
1097    /// Add two colors together and return the result.
1098    fn add(self, rhs: Self) -> Self::Output {
1099        Self::new_with_alpha(
1100            self.r.saturating_add(rhs.r),
1101            self.g.saturating_add(rhs.g),
1102            self.b.saturating_add(rhs.b),
1103            self.a.saturating_add(rhs.a),
1104        )
1105    }
1106}
1107
1108impl Sub for Color {
1109    type Output = Self;
1110
1111    /// Subtract the right hand side from the left hand side and return the result.
1112    fn sub(self, rhs: Self) -> Self::Output {
1113        Self::new_with_alpha(
1114            self.r.saturating_sub(rhs.r),
1115            self.g.saturating_sub(rhs.g),
1116            self.b.saturating_sub(rhs.b),
1117            self.a.saturating_sub(rhs.a),
1118        )
1119    }
1120}
1121
1122impl Mul for Color {
1123    type Output = Self;
1124
1125    /// Multiply two colors together and return the result.
1126    fn mul(self, rhs: Self) -> Self::Output {
1127        Self::new_with_alpha(
1128            (f32::from(self.r) * f32::from(rhs.r) / 255.) as u8,
1129            (f32::from(self.g) * f32::from(rhs.g) / 255.) as u8,
1130            (f32::from(self.b) * f32::from(rhs.b) / 255.) as u8,
1131            (f32::from(self.a) * f32::from(rhs.a) / 255.) as u8,
1132        )
1133    }
1134}
1135
1136impl Mul<f32> for Color {
1137    type Output = Self;
1138
1139    /// Multiply a color with a scalar value and return the result.
1140    fn mul(self, rhs: f32) -> Self::Output {
1141        Self::new_with_alpha(
1142            (f32::from(self.r) * rhs).min(255.0).max(0.0) as u8,
1143            (f32::from(self.g) * rhs).min(255.0).max(0.0) as u8,
1144            (f32::from(self.b) * rhs).min(255.0).max(0.0) as u8,
1145            (f32::from(self.a) * rhs).min(255.0).max(0.0) as u8,
1146        )
1147    }
1148}
1149
1150impl From<Color> for (u8, u8, u8) {
1151    fn from(c: Color) -> Self {
1152        (c.r, c.g, c.b)
1153    }
1154}
1155
1156impl From<(u8, u8, u8)> for Color {
1157    fn from((r, g, b): (u8, u8, u8)) -> Self {
1158        Self::new(r, g, b)
1159    }
1160}
1161
1162#[cfg(feature = "doryen")]
1163impl From<Color> for doryen_rs::Color {
1164    fn from(c: Color) -> Self {
1165        (c.r, c.g, c.b, c.a)
1166    }
1167}
1168
1169#[cfg(feature = "doryen")]
1170impl From<doryen_rs::Color> for Color {
1171    fn from((r, g, b, a): doryen_rs::Color) -> Self {
1172        Self::new_with_alpha(r, g, b, a)
1173    }
1174}
1175
1176/// Color names
1177#[allow(missing_docs)]
1178#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1179#[cfg_attr(
1180    feature = "serialization",
1181    derive(serde_derive::Serialize, serde_derive::Deserialize)
1182)]
1183pub enum Name {
1184    Red,
1185    Flame,
1186    Orange,
1187    Amber,
1188    Yellow,
1189    Lime,
1190    Chartreuse,
1191    Green,
1192    Sea,
1193    Turquoise,
1194    Cyan,
1195    Sky,
1196    Azure,
1197    Blue,
1198    Han,
1199    Violet,
1200    Purple,
1201    Fuchsia,
1202    Magenta,
1203    Pink,
1204    Crimson,
1205}
1206
1207/// Color levels
1208#[allow(missing_docs)]
1209#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1210#[cfg_attr(
1211    feature = "serialization",
1212    derive(serde_derive::Serialize, serde_derive::Deserialize)
1213)]
1214pub enum Level {
1215    Desaturated,
1216    Lightest,
1217    Lighter,
1218    Light,
1219    Normal,
1220    Dark,
1221    Darker,
1222    Darkest,
1223}
1224
1225#[cfg(test)]
1226mod tests {
1227    use crate::color::Color;
1228
1229    #[test]
1230    fn hsv() {
1231        let red = Color::new_hsv(0., 1., 1.);
1232        let green = Color::new_hsv(120., 1., 1.);
1233        let blue = Color::new_hsv(240., 1., 1.);
1234
1235        assert_eq!(red, Color::new(255, 0, 0));
1236        assert_eq!(green, Color::new(0, 255, 0));
1237        assert_eq!(blue, Color::new(0, 0, 255));
1238
1239        let yellow = Color::new_hsv(60., 1., 1.);
1240        let cyan = Color::new_hsv(180., 1., 1.);
1241        let magenta = Color::new_hsv(300., 1., 1.);
1242
1243        assert_eq!(yellow, Color::new(255, 255, 0));
1244        assert_eq!(cyan, Color::new(0, 255, 255));
1245        assert_eq!(magenta, Color::new(255, 0, 255));
1246
1247        let black = Color::new_hsv(0., 0., 0.);
1248        let white = Color::new_hsv(0., 0., 1.);
1249        let gray = Color::new_hsv(0., 0., 0.5);
1250        let silver = Color::new_hsv(0., 0., 0.75);
1251
1252        assert_eq!(black, Color::new(0, 0, 0));
1253        assert_eq!(white, Color::new(255, 255, 255));
1254        assert_eq!(gray, Color::new(128, 128, 128));
1255        assert_eq!(silver, Color::new(191, 191, 191));
1256    }
1257
1258    #[test]
1259    fn lerp() {
1260        let black = Color::BLACK;
1261        let white = Color::WHITE;
1262
1263        let left = black.lerp_rgb(white, 0.0);
1264        let right = black.lerp_rgb(white, 1.0);
1265        let middle = black.lerp_rgb(white, 0.5);
1266
1267        assert_eq!(left, black);
1268        assert_eq!(right, white);
1269        assert_eq!(middle, Color::GRAY);
1270
1271        let left = black.lerp_hsv(white, 0.0);
1272        let right = black.lerp_hsv(white, 1.0);
1273        let middle = black.lerp_hsv(white, 0.5);
1274
1275        assert_eq!(left, black);
1276        assert_eq!(right, white);
1277        assert_eq!(middle, Color::new(128, 128, 128));
1278
1279        let orange = Color::ORANGE;
1280        let cyan = Color::CYAN;
1281
1282        let middle = orange.lerp_rgb(cyan, 0.5);
1283        assert_eq!(middle, Color::new(127, 191, 127));
1284
1285        let middle = orange.lerp_hsv(cyan, 0.5);
1286        assert_eq!(middle, Color::new(64, 255, 0));
1287
1288        let middle = Color::LIGHTEST_RED.lerp_rgb(Color::LIGHT_BLUE, 0.5);
1289        assert_eq!(middle, Color::new(159, 127, 223));
1290
1291        let middle = Color::LIGHTEST_RED.lerp_hsv(Color::LIGHT_BLUE, 0.5);
1292        assert_eq!(middle, Color::LIGHTER_FUCHSIA);
1293    }
1294
1295    #[test]
1296    fn operations() {
1297        let color1 = Color::new(31, 63, 127);
1298        let color2 = Color::new(1, 2, 3);
1299        let color3 = Color::new(50, 100, 200);
1300        assert_eq!(color1 + color2, Color::new(32, 65, 130));
1301        assert_eq!(color1 - color2, Color::new_with_alpha(30, 61, 124, 0));
1302        assert_eq!(color1 * color3, Color::new(6, 24, 99));
1303        assert_eq!(color2 * 2., Color::new(2, 4, 6));
1304    }
1305
1306    #[test]
1307    fn conversions() {
1308        assert_eq!(Color::from((1, 2, 3)), Color::new(1, 2, 3));
1309        assert_eq!((1, 2, 3), Color::new(1, 2, 3).into());
1310        #[cfg(feature = "doryen")]
1311        {
1312            assert_eq!(Color::from((1, 2, 3, 4)), Color::new_with_alpha(1, 2, 3, 4));
1313            assert_eq!((1, 2, 3, 4), Color::new_with_alpha(1, 2, 3, 4).into());
1314        }
1315    }
1316
1317    #[test]
1318    #[allow(clippy::enum_glob_use)]
1319    #[allow(clippy::cognitive_complexity)]
1320    fn by_name_and_level() {
1321        use crate::color::Level::*;
1322        use crate::color::Name::*;
1323
1324        for &n in &[
1325            Red, Flame, Orange, Amber, Yellow, Lime, Chartreuse, Green, Sea, Turquoise, Cyan, Sky,
1326            Azure, Blue, Han, Violet, Purple, Fuchsia, Magenta, Pink, Crimson,
1327        ] {
1328            for &l in &[
1329                Desaturated,
1330                Lightest,
1331                Lighter,
1332                Light,
1333                Normal,
1334                Dark,
1335                Darker,
1336                Darkest,
1337            ] {
1338                let color = Color::by_name_and_level(n, l);
1339
1340                // This is no exact science, clearly, but they all fall within
1341                // fairly narrow ranges.
1342                match n {
1343                    Red => assert!(color.get_hue() < 0.1),
1344                    Flame => assert!((color.get_hue() - 15.).abs() < 0.8),
1345                    Orange => assert!((color.get_hue() - 30.).abs() < 0.5),
1346                    Amber => assert!((color.get_hue() - 45.).abs() < 0.3),
1347                    Yellow => assert!((color.get_hue() - 60.).abs() < 0.1),
1348                    Lime => assert!((color.get_hue() - 75.).abs() < 0.3),
1349                    Chartreuse => assert!((color.get_hue() - 90.).abs() < 0.5),
1350                    Green => assert!((color.get_hue() - 120.).abs() < 0.1),
1351                    Sea => assert!((color.get_hue() - 150.).abs() < 0.5),
1352                    Turquoise => assert!((color.get_hue() - 165.).abs() < 0.3),
1353                    Cyan => assert!((color.get_hue() - 180.).abs() < 0.1),
1354                    Sky => assert!((color.get_hue() - 195.).abs() < 0.3),
1355                    Azure => assert!((color.get_hue() - 210.).abs() < 0.5),
1356                    Blue => assert!((color.get_hue() - 240.).abs() < 0.1),
1357                    Han => assert!((color.get_hue() - 255.).abs() < 0.8),
1358                    Violet => assert!((color.get_hue() - 270.).abs() < 0.5),
1359                    Purple => assert!((color.get_hue() - 285.).abs() < 0.3),
1360                    Fuchsia => assert!((color.get_hue() - 300.).abs() < 0.1),
1361                    Magenta => assert!((color.get_hue() - 315.).abs() < 0.3),
1362                    Pink => assert!((color.get_hue() - 330.).abs() < 0.5),
1363                    Crimson => assert!((color.get_hue() - 345.).abs() < 0.8),
1364                }
1365
1366                match l {
1367                    Desaturated => {
1368                        assert!((color.get_saturation() - 0.5).abs() < 0.1);
1369                        assert!((color.get_value() - 0.5).abs() < 0.1);
1370                    }
1371                    Lightest => {
1372                        assert!((color.get_saturation() - 0.25).abs() < 0.1);
1373                        assert!((color.get_value() - 1.0).abs() < 0.1);
1374                    }
1375                    Lighter => {
1376                        assert!((color.get_saturation() - 0.5).abs() < 0.1);
1377                        assert!((color.get_value() - 1.0).abs() < 0.1);
1378                    }
1379                    Light => {
1380                        assert!((color.get_saturation() - 0.75).abs() < 0.1);
1381                        assert!((color.get_value() - 1.0).abs() < 0.1);
1382                    }
1383                    Normal => {
1384                        assert!((color.get_saturation() - 1.0).abs() < 0.1);
1385                        assert!((color.get_value() - 1.0).abs() < 0.1);
1386                    }
1387                    Dark => {
1388                        assert!((color.get_saturation() - 1.0).abs() < 0.1);
1389                        assert!((color.get_value() - 0.75).abs() < 0.1);
1390                    }
1391                    Darker => {
1392                        assert!((color.get_saturation() - 1.0).abs() < 0.1);
1393                        assert!((color.get_value() - 0.5).abs() < 0.1);
1394                    }
1395                    Darkest => {
1396                        assert!((color.get_saturation() - 1.0).abs() < 0.1);
1397                        assert!((color.get_value() - 0.25).abs() < 0.1);
1398                    }
1399                }
1400            }
1401        }
1402    }
1403}