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 & SEPIA</th></tr>
73//! <tr><td colspan="2"> </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> </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> </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}