1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "Colour.h"
11 
12 #include "../core/EnumMap.hpp"
13 #include "../drawing/Drawing.h"
14 #include "../sprites.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 
19 rct_colour_map ColourMapA[COLOUR_COUNT] = {};
20 
21 enum
22 {
23     INDEX_COLOUR_0 = 243,
24     INDEX_COLOUR_1 = 245,
25     INDEX_DARKEST = 245,
26     INDEX_DARKER = 246,
27     INDEX_DARK = 247,
28     INDEX_MID_DARK = 248,
29     INDEX_MID_LIGHT = 249,
30     INDEX_LIGHT = 250,
31     INDEX_LIGHTER = 251,
32     INDEX_LIGHTEST = 252,
33     INDEX_COLOUR_10 = 253,
34     INDEX_COLOUR_11 = 254,
35 };
36 
colours_init_maps()37 void colours_init_maps()
38 {
39     // Get colour maps from g1
40     for (int32_t i = 0; i < COLOUR_COUNT; i++)
41     {
42         const rct_g1_element* g1 = gfx_get_g1_element(SPR_PALETTE_2_START + i);
43         if (g1 != nullptr)
44         {
45             ColourMapA[i].colour_0 = g1->offset[INDEX_COLOUR_0];
46             ColourMapA[i].colour_1 = g1->offset[INDEX_COLOUR_1];
47             ColourMapA[i].darkest = g1->offset[INDEX_DARKEST];
48             ColourMapA[i].darker = g1->offset[INDEX_DARKER];
49             ColourMapA[i].dark = g1->offset[INDEX_DARK];
50             ColourMapA[i].mid_dark = g1->offset[INDEX_MID_DARK];
51             ColourMapA[i].mid_light = g1->offset[INDEX_MID_LIGHT];
52             ColourMapA[i].light = g1->offset[INDEX_LIGHT];
53             ColourMapA[i].lighter = g1->offset[INDEX_LIGHTER];
54             ColourMapA[i].lightest = g1->offset[INDEX_LIGHTEST];
55             ColourMapA[i].colour_10 = g1->offset[INDEX_COLOUR_10];
56             ColourMapA[i].colour_11 = g1->offset[INDEX_COLOUR_11];
57         }
58     }
59 }
60 
61 namespace Colour
62 {
63     static const EnumMap<colour_t> LookupTable{
64         { "black", COLOUR_BLACK },
65         { "grey", COLOUR_GREY },
66         { "white", COLOUR_WHITE },
67         { "dark_purple", COLOUR_DARK_PURPLE },
68         { "light_purple", COLOUR_LIGHT_PURPLE },
69         { "bright_purple", COLOUR_BRIGHT_PURPLE },
70         { "dark_blue", COLOUR_DARK_BLUE },
71         { "light_blue", COLOUR_LIGHT_BLUE },
72         { "icy_blue", COLOUR_ICY_BLUE },
73         { "teal", COLOUR_TEAL },
74         { "aquamarine", COLOUR_AQUAMARINE },
75         { "saturated_green", COLOUR_SATURATED_GREEN },
76         { "dark_green", COLOUR_DARK_GREEN },
77         { "moss_green", COLOUR_MOSS_GREEN },
78         { "bright_green", COLOUR_BRIGHT_GREEN },
79         { "olive_green", COLOUR_OLIVE_GREEN },
80         { "dark_olive_green", COLOUR_DARK_OLIVE_GREEN },
81         { "bright_yellow", COLOUR_BRIGHT_YELLOW },
82         { "yellow", COLOUR_YELLOW },
83         { "dark_yellow", COLOUR_DARK_YELLOW },
84         { "light_orange", COLOUR_LIGHT_ORANGE },
85         { "dark_orange", COLOUR_DARK_ORANGE },
86         { "light_brown", COLOUR_LIGHT_BROWN },
87         { "saturated_brown", COLOUR_SATURATED_BROWN },
88         { "dark_brown", COLOUR_DARK_BROWN },
89         { "salmon_pink", COLOUR_SALMON_PINK },
90         { "bordeaux_red", COLOUR_BORDEAUX_RED },
91         { "saturated_red", COLOUR_SATURATED_RED },
92         { "bright_red", COLOUR_BRIGHT_RED },
93         { "dark_pink", COLOUR_DARK_PINK },
94         { "bright_pink", COLOUR_BRIGHT_PINK },
95         { "light_pink", COLOUR_LIGHT_PINK },
96     };
97 
FromString(std::string_view s,colour_t defaultValue)98     colour_t FromString(std::string_view s, colour_t defaultValue)
99     {
100         auto result = LookupTable.find(s);
101         return (result != LookupTable.end()) ? result->second : defaultValue;
102     }
103 
104 } // namespace Colour
105 
106 #ifndef NO_TTF
107 static uint8_t BlendColourMap[PALETTE_COUNT][PALETTE_COUNT] = { 0 };
108 
findClosestPaletteIndex(uint8_t red,uint8_t green,uint8_t blue)109 static uint8_t findClosestPaletteIndex(uint8_t red, uint8_t green, uint8_t blue)
110 {
111     int16_t closest = -1;
112     int32_t closestDistance = INT32_MAX;
113 
114     for (int i = PALETTE_INDEX_0; i < PALETTE_INDEX_230; i++)
115     {
116         const int32_t distance = std::pow(gPalette[i].Red - red, 2) + std::pow(gPalette[i].Green - green, 2)
117             + std::pow(gPalette[i].Blue - blue, 2);
118 
119         if (distance < closestDistance)
120         {
121             closest = i;
122             closestDistance = distance;
123         }
124     }
125 
126     return closest;
127 }
128 
blendColours(const uint8_t paletteIndex1,const uint8_t paletteIndex2)129 uint8_t blendColours(const uint8_t paletteIndex1, const uint8_t paletteIndex2)
130 {
131     const uint8_t cMin = std::min(paletteIndex1, paletteIndex2);
132     const uint8_t cMax = std::max(paletteIndex1, paletteIndex2);
133 
134     if (BlendColourMap[cMin][cMax] != 0)
135     {
136         return BlendColourMap[cMin][cMax];
137     }
138 
139     uint8_t red = (gPalette[cMin].Red + gPalette[cMax].Red) / 2;
140     uint8_t green = (gPalette[cMin].Green + gPalette[cMax].Green) / 2;
141     uint8_t blue = (gPalette[cMin].Blue + gPalette[cMax].Blue) / 2;
142 
143     BlendColourMap[cMin][cMax] = findClosestPaletteIndex(red, green, blue);
144     return BlendColourMap[cMin][cMax];
145 }
146 #endif
147