Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/Assertions.h>
28#include <AK/BufferStream.h>
29#include <AK/Optional.h>
30#include <AK/String.h>
31#include <LibGfx/Color.h>
32#include <LibGfx/SystemTheme.h>
33#include <ctype.h>
34#include <stdio.h>
35
36namespace Gfx {
37
38Color::Color(NamedColor named)
39{
40 struct {
41 u8 r;
42 u8 g;
43 u8 b;
44 } rgb;
45
46 switch (named) {
47 case Black:
48 rgb = { 0, 0, 0 };
49 break;
50 case White:
51 rgb = { 255, 255, 255 };
52 break;
53 case Red:
54 rgb = { 255, 0, 0 };
55 break;
56 case Green:
57 rgb = { 0, 255, 0 };
58 break;
59 case Cyan:
60 rgb = { 0, 255, 255 };
61 break;
62 case DarkCyan:
63 rgb = { 0, 127, 127 };
64 break;
65 case MidCyan:
66 rgb = { 0, 192, 192 };
67 break;
68 case Blue:
69 rgb = { 0, 0, 255 };
70 break;
71 case Yellow:
72 rgb = { 255, 255, 0 };
73 break;
74 case Magenta:
75 rgb = { 255, 0, 255 };
76 break;
77 case DarkGray:
78 rgb = { 64, 64, 64 };
79 break;
80 case MidGray:
81 rgb = { 127, 127, 127 };
82 break;
83 case LightGray:
84 rgb = { 192, 192, 192 };
85 break;
86 case MidGreen:
87 rgb = { 0, 192, 0 };
88 break;
89 case MidBlue:
90 rgb = { 0, 0, 192 };
91 break;
92 case MidRed:
93 rgb = { 192, 0, 0 };
94 break;
95 case MidMagenta:
96 rgb = { 192, 0, 192 };
97 break;
98 case DarkGreen:
99 rgb = { 0, 128, 0 };
100 break;
101 case DarkBlue:
102 rgb = { 0, 0, 128 };
103 break;
104 case DarkRed:
105 rgb = { 128, 0, 0 };
106 break;
107 case WarmGray:
108 rgb = { 212, 208, 200 };
109 break;
110 default:
111 ASSERT_NOT_REACHED();
112 break;
113 }
114
115 m_value = 0xff000000 | (rgb.r << 16) | (rgb.g << 8) | rgb.b;
116}
117
118String Color::to_string() const
119{
120 return String::format("#%b%b%b%b", red(), green(), blue(), alpha());
121}
122
123Optional<Color> Color::from_string(const StringView& string)
124{
125 if (string.is_empty())
126 return {};
127
128 struct ColorAndWebName {
129 RGBA32 color;
130 const char* name;
131 };
132
133 const ColorAndWebName web_colors[] = {
134 // CSS Level 1
135 { 0x000000, "black" },
136 { 0xc0c0c0, "silver" },
137 { 0x808080, "gray" },
138 { 0xffffff, "white" },
139 { 0x800000, "maroon" },
140 { 0xff0000, "red" },
141 { 0x800080, "purple" },
142 { 0xff00ff, "fuchsia" },
143 { 0x008000, "green" },
144 { 0x00ff00, "lime" },
145 { 0x808000, "olive" },
146 { 0xffff00, "yellow" },
147 { 0x000080, "navy" },
148 { 0x0000ff, "blue" },
149 { 0x008080, "teal" },
150 { 0x00ffff, "aqua" },
151 // CSS Level 2 (Revision 1)
152 { 0xffa500, "orange" },
153 // CSS Color Module Level 3
154 { 0xf0f8ff, "aliceblue" },
155 { 0xfaebd7, "antiquewhite" },
156 { 0x7fffd4, "aquamarine" },
157 { 0xf0ffff, "azure" },
158 { 0xf5f5dc, "beige" },
159 { 0xffe4c4, "bisque" },
160 { 0xffebcd, "blanchedalmond" },
161 { 0x8a2be2, "blueviolet" },
162 { 0xa52a2a, "brown" },
163 { 0xdeb887, "burlywood" },
164 { 0x5f9ea0, "cadetblue" },
165 { 0x7fff00, "chartreuse" },
166 { 0xd2691e, "chocolate" },
167 { 0xff7f50, "coral" },
168 { 0x6495ed, "cornflowerblue" },
169 { 0xfff8dc, "cornsilk" },
170 { 0xdc143c, "crimson" },
171 { 0x00ffff, "cyan" },
172 { 0x00008b, "darkblue" },
173 { 0x008b8b, "darkcyan" },
174 { 0xb8860b, "darkgoldenrod" },
175 { 0xa9a9a9, "darkgray" },
176 { 0x006400, "darkgreen" },
177 { 0xa9a9a9, "darkgrey" },
178 { 0xbdb76b, "darkkhaki" },
179 { 0x8b008b, "darkmagenta" },
180 { 0x556b2f, "darkolivegreen" },
181 { 0xff8c00, "darkorange" },
182 { 0x9932cc, "darkorchid" },
183 { 0x8b0000, "darkred" },
184 { 0xe9967a, "darksalmon" },
185 { 0x8fbc8f, "darkseagreen" },
186 { 0x483d8b, "darkslateblue" },
187 { 0x2f4f4f, "darkslategray" },
188 { 0x2f4f4f, "darkslategrey" },
189 { 0x00ced1, "darkturquoise" },
190 { 0x9400d3, "darkviolet" },
191 { 0xff1493, "deeppink" },
192 { 0x00bfff, "deepskyblue" },
193 { 0x696969, "dimgray" },
194 { 0x696969, "dimgrey" },
195 { 0x1e90ff, "dodgerblue" },
196 { 0xb22222, "firebrick" },
197 { 0xfffaf0, "floralwhite" },
198 { 0x228b22, "forestgreen" },
199 { 0xdcdcdc, "gainsboro" },
200 { 0xf8f8ff, "ghostwhite" },
201 { 0xffd700, "gold" },
202 { 0xdaa520, "goldenrod" },
203 { 0xadff2f, "greenyellow" },
204 { 0x808080, "grey" },
205 { 0xf0fff0, "honeydew" },
206 { 0xff69b4, "hotpink" },
207 { 0xcd5c5c, "indianred" },
208 { 0x4b0082, "indigo" },
209 { 0xfffff0, "ivory" },
210 { 0xf0e68c, "khaki" },
211 { 0xe6e6fa, "lavender" },
212 { 0xfff0f5, "lavenderblush" },
213 { 0x7cfc00, "lawngreen" },
214 { 0xfffacd, "lemonchiffon" },
215 { 0xadd8e6, "lightblue" },
216 { 0xf08080, "lightcoral" },
217 { 0xe0ffff, "lightcyan" },
218 { 0xfafad2, "lightgoldenrody" },
219 { 0xd3d3d3, "lightgray" },
220 { 0x90ee90, "lightgreen" },
221 { 0xd3d3d3, "lightgrey" },
222 { 0xffb6c1, "lightpink" },
223 { 0xffa07a, "lightsalmon" },
224 { 0x20b2aa, "lightseagreen" },
225 { 0x87cefa, "lightskyblue" },
226 { 0x778899, "lightslategray" },
227 { 0x778899, "lightslategrey" },
228 { 0xb0c4de, "lightsteelblue" },
229 { 0xffffe0, "lightyellow" },
230 { 0x32cd32, "limegreen" },
231 { 0xfaf0e6, "linen" },
232 { 0xff00ff, "magenta" },
233 { 0x66cdaa, "mediumaquamarin" },
234 { 0x0000cd, "mediumblue" },
235 { 0xba55d3, "mediumorchid" },
236 { 0x9370db, "mediumpurple" },
237 { 0x3cb371, "mediumseagreen" },
238 { 0x7b68ee, "mediumslateblue" },
239 { 0x00fa9a, "mediumspringgre" },
240 { 0x48d1cc, "mediumturquoise" },
241 { 0xc71585, "mediumvioletred" },
242 { 0x191970, "midnightblue" },
243 { 0xf5fffa, "mintcream" },
244 { 0xffe4e1, "mistyrose" },
245 { 0xffe4b5, "moccasin" },
246 { 0xffdead, "navajowhite" },
247 { 0xfdf5e6, "oldlace" },
248 { 0x6b8e23, "olivedrab" },
249 { 0xff4500, "orangered" },
250 { 0xda70d6, "orchid" },
251 { 0xeee8aa, "palegoldenrod" },
252 { 0x98fb98, "palegreen" },
253 { 0xafeeee, "paleturquoise" },
254 { 0xdb7093, "palevioletred" },
255 { 0xffefd5, "papayawhip" },
256 { 0xffdab9, "peachpuff" },
257 { 0xcd853f, "peru" },
258 { 0xffc0cb, "pink" },
259 { 0xdda0dd, "plum" },
260 { 0xb0e0e6, "powderblue" },
261 { 0xbc8f8f, "rosybrown" },
262 { 0x4169e1, "royalblue" },
263 { 0x8b4513, "saddlebrown" },
264 { 0xfa8072, "salmon" },
265 { 0xf4a460, "sandybrown" },
266 { 0x2e8b57, "seagreen" },
267 { 0xfff5ee, "seashell" },
268 { 0xa0522d, "sienna" },
269 { 0x87ceeb, "skyblue" },
270 { 0x6a5acd, "slateblue" },
271 { 0x708090, "slategray" },
272 { 0x708090, "slategrey" },
273 { 0xfffafa, "snow" },
274 { 0x00ff7f, "springgreen" },
275 { 0x4682b4, "steelblue" },
276 { 0xd2b48c, "tan" },
277 { 0xd8bfd8, "thistle" },
278 { 0xff6347, "tomato" },
279 { 0x40e0d0, "turquoise" },
280 { 0xee82ee, "violet" },
281 { 0xf5deb3, "wheat" },
282 { 0xf5f5f5, "whitesmoke" },
283 { 0x9acd32, "yellowgreen" },
284 // CSS Color Module Level 4
285 { 0x663399, "rebeccapurple" },
286 // (Fallback)
287 { 0x000000, nullptr }
288 };
289
290 for (size_t i = 0; web_colors[i].name; ++i) {
291 if (string == web_colors[i].name)
292 return Color::from_rgb(web_colors[i].color);
293 }
294
295 if (string[0] != '#')
296 return {};
297
298 auto hex_nibble_to_u8 = [](char nibble) -> Optional<u8> {
299 if (!isxdigit(nibble))
300 return {};
301 if (nibble >= '0' && nibble <= '9')
302 return nibble - '0';
303 return 10 + (tolower(nibble) - 'a');
304 };
305
306 if (string.length() == 4) {
307 Optional<u8> r = hex_nibble_to_u8(string[1]);
308 Optional<u8> g = hex_nibble_to_u8(string[2]);
309 Optional<u8> b = hex_nibble_to_u8(string[3]);
310 if (!r.has_value() || !g.has_value() || !b.has_value())
311 return {};
312 return Color(r.value() * 17, g.value() * 17, b.value() * 17);
313 }
314
315 if (string.length() == 5) {
316 Optional<u8> r = hex_nibble_to_u8(string[1]);
317 Optional<u8> g = hex_nibble_to_u8(string[2]);
318 Optional<u8> b = hex_nibble_to_u8(string[3]);
319 Optional<u8> a = hex_nibble_to_u8(string[4]);
320 if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
321 return {};
322 return Color(r.value() * 17, g.value() * 17, b.value() * 17, a.value() * 17);
323 }
324
325 if (string.length() != 7 && string.length() != 9)
326 return {};
327
328 auto to_hex = [&](char c1, char c2) -> Optional<u8> {
329 auto nib1 = hex_nibble_to_u8(c1);
330 auto nib2 = hex_nibble_to_u8(c2);
331 if (!nib1.has_value() || !nib2.has_value())
332 return {};
333 return nib1.value() << 4 | nib2.value();
334 };
335
336 Optional<u8> r = to_hex(string[1], string[2]);
337 Optional<u8> g = to_hex(string[3], string[4]);
338 Optional<u8> b = to_hex(string[5], string[6]);
339 Optional<u8> a = string.length() == 9 ? to_hex(string[7], string[8]) : Optional<u8>(255);
340
341 if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
342 return {};
343
344 return Color(r.value(), g.value(), b.value(), a.value());
345}
346}
347
348const LogStream& operator<<(const LogStream& stream, Color value)
349{
350 return stream << value.to_string();
351}
352
353bool IPC::decode(BufferStream& stream, Color& color)
354{
355 u32 rgba = 0;
356 stream >> rgba;
357 if (stream.handle_read_failure())
358 return false;
359 color = Color::from_rgba(rgba);
360 return true;
361}