Current Version: 1.0.33
Project Name: csspp
color.cpp
Go to the documentation of this file.
1// Copyright (c) 2015-2025 Made to Order Software Corp. All Rights Reserved
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License along
14// with this program; if not, write to the Free Software Foundation, Inc.,
15// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
30// self
31//
32#include "csspp/lexer.h"
33
34#include "csspp/exception.h"
35
36
37// C++
38//
39#include <cmath>
40#include <iomanip>
41#include <iostream>
42
43
44// last include
45//
46#include <snapdev/poison.h>
47
48
49
50namespace csspp
51{
52
53namespace
54{
55
57{
58 { 240,248,255,255, "aliceblue" },
59 { 250,235,215,255, "antiquewhite" },
60 { 0,255,255,255, "aqua" },
61 { 127,255,212,255, "aquamarine" },
62 { 240,255,255,255, "azure" },
63 { 245,245,220,255, "beige" },
64 { 255,228,196,255, "bisque" },
65 { 0, 0, 0,255, "black" },
66 { 255,235,205,255, "blanchedalmond" },
67 { 0, 0,255,255, "blue" },
68 { 138, 43,226,255, "blueviolet" },
69 { 165, 42, 42,255, "brown" },
70 { 222,184,135,255, "burlywood" },
71 { 95,158,160,255, "cadetblue" },
72 { 127,255, 0,255, "chartreuse" },
73 { 210,105, 30,255, "chocolate" },
74 { 255,127, 80,255, "coral" },
75 { 100,149,237,255, "cornflowerblue" },
76 { 255,248,220,255, "cornsilk" },
77 { 220, 20, 60,255, "crimson" },
78 { 0,255,255,255, "cyan" },
79 { 0, 0,139,255, "darkblue" },
80 { 0,139,139,255, "darkcyan" },
81 { 184,134, 11,255, "darkgoldenrod" },
82 { 169,169,169,255, "darkgray" },
83 { 0,100, 0,255, "darkgreen" },
84 { 169,169,169,255, "darkgrey" },
85 { 189,183,107,255, "darkkhaki" },
86 { 139, 0,139,255, "darkmagenta" },
87 { 85,107, 47,255, "darkolivegreen" },
88 { 255,140, 0,255, "darkorange" },
89 { 153, 50,204,255, "darkorchid" },
90 { 139, 0, 0,255, "darkred" },
91 { 233,150,122,255, "darksalmon" },
92 { 143,188,143,255, "darkseagreen" },
93 { 72, 61,139,255, "darkslateblue" },
94 { 47, 79, 79,255, "darkslategray" },
95 { 47, 79, 79,255, "darkslategrey" },
96 { 0,206,209,255, "darkturquoise" },
97 { 148, 0,211,255, "darkviolet" },
98 { 255, 20,147,255, "deeppink" },
99 { 0,191,255,255, "deepskyblue" },
100 { 105,105,105,255, "dimgray" },
101 { 105,105,105,255, "dimgrey" },
102 { 30,144,255,255, "dodgerblue" },
103 { 178, 34, 34,255, "firebrick" },
104 { 255,250,240,255, "floralwhite" },
105 { 34,139, 34,255, "forestgreen" },
106 { 255, 0,255,255, "fuchsia" },
107 { 220,220,220,255, "gainsboro" },
108 { 248,248,255,255, "ghostwhite" },
109 { 255,215, 0,255, "gold" },
110 { 218,165, 32,255, "goldenrod" },
111 { 128,128,128,255, "gray" },
112 { 0,128, 0,255, "green" },
113 { 173,255, 47,255, "greenyellow" },
114 { 128,128,128,255, "grey" },
115 { 240,255,240,255, "honeydew" },
116 { 255,105,180,255, "hotpink" },
117 { 205, 92, 92,255, "indianred" },
118 { 75, 0,130,255, "indigo" },
119 { 255,255,240,255, "ivory" },
120 { 240,230,140,255, "khaki" },
121 { 230,230,250,255, "lavender" },
122 { 255,240,245,255, "lavenderblush" },
123 { 124,252, 0,255, "lawngreen" },
124 { 255,250,205,255, "lemonchiffon" },
125 { 173,216,230,255, "lightblue" },
126 { 240,128,128,255, "lightcoral" },
127 { 224,255,255,255, "lightcyan" },
128 { 250,250,210,255, "lightgoldenrodyellow" },
129 { 211,211,211,255, "lightgray" },
130 { 144,238,144,255, "lightgreen" },
131 { 211,211,211,255, "lightgrey" },
132 { 255,182,193,255, "lightpink" },
133 { 255,160,122,255, "lightsalmon" },
134 { 32,178,170,255, "lightseagreen" },
135 { 135,206,250,255, "lightskyblue" },
136 { 119,136,153,255, "lightslategray" },
137 { 119,136,153,255, "lightslategrey" },
138 { 176,196,222,255, "lightsteelblue" },
139 { 255,255,224,255, "lightyellow" },
140 { 0,255, 0,255, "lime" },
141 { 50,205, 50,255, "limegreen" },
142 { 250,240,230,255, "linen" },
143 { 255, 0,255,255, "magenta" },
144 { 128, 0, 0,255, "maroon" },
145 { 102,205,170,255, "mediumaquamarine" },
146 { 0, 0,205,255, "mediumblue" },
147 { 186, 85,211,255, "mediumorchid" },
148 { 147,112,219,255, "mediumpurple" },
149 { 60,179,113,255, "mediumseagreen" },
150 { 123,104,238,255, "mediumslateblue" },
151 { 0,250,154,255, "mediumspringgreen" },
152 { 72,209,204,255, "mediumturquoise" },
153 { 199, 21,133,255, "mediumvioletred" },
154 { 25, 25,112,255, "midnightblue" },
155 { 245,255,250,255, "mintcream" },
156 { 255,228,225,255, "mistyrose" },
157 { 255,228,181,255, "moccasin" },
158 { 255,222,173,255, "navajowhite" },
159 { 0, 0,128,255, "navy" },
160 { 253,245,230,255, "oldlace" },
161 { 128,128, 0,255, "olive" },
162 { 107,142, 35,255, "olivedrab" },
163 { 255,165, 0,255, "orange" },
164 { 255, 69, 0,255, "orangered" },
165 { 218,112,214,255, "orchid" },
166 { 238,232,170,255, "palegoldenrod" },
167 { 152,251,152,255, "palegreen" },
168 { 175,238,238,255, "paleturquoise" },
169 { 219,112,147,255, "palevioletred" },
170 { 255,239,213,255, "papayawhip" },
171 { 255,218,185,255, "peachpuff" },
172 { 205,133, 63,255, "peru" },
173 { 255,192,203,255, "pink" },
174 { 221,160,221,255, "plum" },
175 { 176,224,230,255, "powderblue" },
176 { 128, 0,128,255, "purple" },
177 { 255, 0, 0,255, "red" },
178 { 188,143,143,255, "rosybrown" },
179 { 65,105,225,255, "royalblue" },
180 { 139, 69, 19,255, "saddlebrown" },
181 { 250,128,114,255, "salmon" },
182 { 244,164, 96,255, "sandybrown" },
183 { 46,139, 87,255, "seagreen" },
184 { 255,245,238,255, "seashell" },
185 { 160, 82, 45,255, "sienna" },
186 { 192,192,192,255, "silver" },
187 { 135,206,235,255, "skyblue" },
188 { 106, 90,205,255, "slateblue" },
189 { 112,128,144,255, "slategray" },
190 { 112,128,144,255, "slategrey" },
191 { 255,250,250,255, "snow" },
192 { 0,255,127,255, "springgreen" },
193 { 70,130,180,255, "steelblue" },
194 { 210,180,140,255, "tan" },
195 { 0,128,128,255, "teal" },
196 { 216,191,216,255, "thistle" },
197 { 255, 99, 71,255, "tomato" },
198 { 0, 0, 0, 0, "transparent" },
199 { 64,224,208,255, "turquoise" },
200 { 238,130,238,255, "violet" },
201 { 245,222,179,255, "wheat" },
202 { 255,255,255,255, "white" },
203 { 245,245,245,255, "whitesmoke" },
204 { 255,255, 0,255, "yellow" },
205 { 154,205, 50,255, "yellowgreen" }
206};
207
208size_t const color_names_size(sizeof(color_names) / sizeof(color_names[0]));
209
211{
212 // first clamp
213 if(c >= static_cast<color_component_t>(1.0))
214 {
215 return 255;
216 }
217 else if(c <= static_cast<color_component_t>(0.0))
218 {
219 return 0;
220 }
221
222 // in range, compute the corresponding value
223 return static_cast<byte_component_t>(c * static_cast<color_component_t>(255.0) + static_cast<color_component_t>(0.5));
224}
225
226} // no name namespace
227
228void color::set_color(uint32_t const rgba)
229{
230 set_color((rgba >> 0) & 255,
231 (rgba >> 8) & 255,
232 (rgba >> 16) & 255,
233 (rgba >> 24) & 255);
234}
235
237{
238 f_red = static_cast<color_component_t>(red) / static_cast<color_component_t>(255.0);
239 f_green = static_cast<color_component_t>(green) / static_cast<color_component_t>(255.0);
240 f_blue = static_cast<color_component_t>(blue) / static_cast<color_component_t>(255.0);
241 f_alpha = static_cast<color_component_t>(alpha) / static_cast<color_component_t>(255.0);
242}
243
244void color::set_color(int red, int green, int blue, int alpha)
245{
246 f_red = static_cast<color_component_t>(red) / static_cast<color_component_t>(255.0);
247 f_green = static_cast<color_component_t>(green) / static_cast<color_component_t>(255.0);
248 f_blue = static_cast<color_component_t>(blue) / static_cast<color_component_t>(255.0);
249 f_alpha = static_cast<color_component_t>(alpha) / static_cast<color_component_t>(255.0);
250}
251
252void color::set_color(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
253{
254 f_red = static_cast<color_component_t>(red) / static_cast<color_component_t>(255.0);
255 f_green = static_cast<color_component_t>(green) / static_cast<color_component_t>(255.0);
256 f_blue = static_cast<color_component_t>(blue) / static_cast<color_component_t>(255.0);
257 f_alpha = static_cast<color_component_t>(alpha) / static_cast<color_component_t>(255.0);
258}
259
261{
262 f_red = red;
263 f_green = green;
264 f_blue = blue;
265 f_alpha = alpha;
266}
267
268void color::set_color(double red, double green, double blue, double alpha)
269{
270 f_red = red;
271 f_green = green;
272 f_blue = blue;
273 f_alpha = alpha;
274}
275
276bool color::set_color(std::string const & name, bool name_only)
277{
278 // we assume that the color name was written as an identifier and thus
279 // it is already in lowercase and can be compared directly
280 {
281 size_t i(0);
282 size_t j(color_names_size);
283#ifdef _DEBUG
284 for(size_t k(1); k < color_names_size; ++k)
285 {
286 if(std::string(color_names[k - 1].f_name) >= color_names[k].f_name)
287 {
288 throw csspp_exception_logic("colors are not in alphabetical order, our binary search would break."); // LCOV_EXCL_LINE
289 }
290 }
291#endif
292 while(i < j)
293 {
294 size_t const p((j - i) / 2 + i);
295 if(color_names[p].f_name == name)
296 {
297 set_color(color_names[p].f_red,
298 color_names[p].f_green,
299 color_names[p].f_blue,
300 color_names[p].f_alpha);
301 return true;
302 }
303 if(color_names[p].f_name < name)
304 {
305 i = p + 1;
306 }
307 else
308 {
309 j = p;
310 }
311 }
312 }
313
314 if(!name_only)
315 {
316
317 // if not a direct name, it has to be a valid hexadecimal string
318 // of 3 or 6 digits
319 if(name.length() == 3)
320 {
321 if(lexer::is_hex(name[0])
322 && lexer::is_hex(name[1])
323 && lexer::is_hex(name[2]))
324 {
325 set_color(lexer::hex_to_dec(name[0]) * 0x11,
326 lexer::hex_to_dec(name[1]) * 0x11,
327 lexer::hex_to_dec(name[2]) * 0x11,
328 255);
329 return true;
330 }
331 }
332 else if(name.length() == 6)
333 {
334 if(lexer::is_hex(name[0])
335 && lexer::is_hex(name[1])
336 && lexer::is_hex(name[2])
337 && lexer::is_hex(name[3])
338 && lexer::is_hex(name[4])
339 && lexer::is_hex(name[5]))
340 {
341 set_color(lexer::hex_to_dec(name[0]) * 16 + lexer::hex_to_dec(name[1]),
342 lexer::hex_to_dec(name[2]) * 16 + lexer::hex_to_dec(name[3]),
343 lexer::hex_to_dec(name[4]) * 16 + lexer::hex_to_dec(name[5]),
344 255);
345 return true;
346 }
347 }
348
349 }
350
351 return false;
352}
353
355{
356 // see: http://en.wikipedia.org/wiki/HSL_and_HSV
357 double const chroma = (1.0 - fabs(2.0 * lightness - 1.0)) * saturation;
358 double const h1 = 6.0 * fmod(hue, M_PI * 2.0) / (M_PI * 2.0);
359 double const x = chroma * (1.0 - fabs(fmod(h1, 2) - 1.0));
360 double r, g, b;
361
362 if(h1 >= 0.0 && h1 < 1.0)
363 {
364 r = chroma;
365 g = x;
366 b = 0.0;
367 }
368 else if(h1 >= 1.0 && h1 < 2.0)
369 {
370 r = x;
371 g = chroma;
372 b = 0.0;
373 }
374 else if(h1 >= 2.0 && h1 < 3.0)
375 {
376 r = 0.0;
377 g = chroma;
378 b = x;
379 }
380 else if(h1 >= 3.0 && h1 < 4.0)
381 {
382 r = 0.0;
383 g = x;
384 b = chroma;
385 }
386 else if(h1 >= 4.0 && h1 < 5.0)
387 {
388 r = x;
389 g = 0.0;
390 b = chroma;
391 }
392 else if(h1 >= 5.0 && h1 < 6.0)
393 {
394 r = chroma;
395 g = 0;
396 b = x;
397 }
398 else
399 {
400 // negative hues generally end up here
401 r = 0.0;
402 g = 0.0;
403 b = 0.0;
404 }
405
406 double const m(lightness - 0.5 * chroma);
407
408 f_red = static_cast<color_component_t>(r + m);
409 f_green = static_cast<color_component_t>(g + m);
410 f_blue = static_cast<color_component_t>(b + m);
411
412 f_alpha = alpha;
413}
414
415void color::get_hsl(color_component_t & hue, color_component_t & saturation, color_component_t & lightness, color_component_t & alpha) const
416{
417#pragma GCC diagnostic push
418#pragma GCC diagnostic ignored "-Wfloat-equal"
419 // see: http://en.wikipedia.org/wiki/HSL_and_HSV
420 double const maximum(std::max(f_red, std::max(f_green, f_blue)));
421 double const minimum(std::min(f_red, std::min(f_green, f_blue)));
422 double const chroma(maximum - minimum);
423
424//std::cerr << " min/max/C " << minimum << "/" << maximum << "/" << chroma;
425
426 // warning: we define chroma in degrees just as set_hsl() expects
427 // but most of our angles are kept in radians otherwise
428 if(chroma == 0.0)
429 {
430 // this is really "undefined"...
431 // absolutely any angle would work just fine
432 hue = 0.0;
433 }
434 else if(maximum == f_red)
435 {
436 hue = (f_green - f_blue) / chroma;
437 }
438 else if(maximum == f_green)
439 {
440 hue = (f_blue - f_red) / chroma + 2.0;
441 }
442 else //if(maximum == f_blue)
443 {
444 hue = (f_red - f_green) / chroma + 4.0;
445 }
446 if(hue < 0)
447 {
448 hue += 6.0;
449 }
450 hue = fmod(hue, 6.0) / 6.0 * M_PI * 2.0;
451
452 lightness = (minimum + maximum) / 2.0;
453
454 if(lightness == 0.0 || lightness == 1.0)
455 {
456 saturation = 0.0;
457 }
458 else
459 {
460 saturation = chroma / (1.0 - fabs(lightness * 2.0 - 1.0));
461 }
462
463 alpha = f_alpha;
464#pragma GCC diagnostic pop
465}
466
468{
469 // the color_component_to_byte() function clamps each color component
470 // between 0 and 1 before converting it to a uint8_t
471 return (color_component_to_byte(f_red) << 0)
472 | (color_component_to_byte(f_green) << 8)
473 | (color_component_to_byte(f_blue) << 16)
474 | (color_component_to_byte(f_alpha) << 24);
475}
476
478{
479 red = f_red;
480 green = f_green;
481 blue = f_blue;
482 alpha = f_alpha;
483}
484
485bool color::is_solid() const
486{
487 return f_alpha >= 1.0;
488}
489
491{
492 return f_alpha <= 0.0;
493}
494
495std::string color::to_string() const
496{
497 rgba_color_t const col(get_color());
498
499 byte_component_t const red ((col >> 0) & 255);
500 byte_component_t const green((col >> 8) & 255);
501 byte_component_t const blue ((col >> 16) & 255);
502 byte_component_t const alpha((col >> 24) & 255);
503
504 if(alpha == 255) // is_solid()
505 {
506 // we will have to do some testing, but with compression, always
507 // use #RGB or #RRGGBB is probably better than saving 1 character
508 // here or there... (because compression is all about repeated
509 // bytes that can be saved in a small number of bits.)
510 switch(col)
511 {
512 case (192UL << 0) | (192UL << 8) | (192UL << 16) | (255UL << 24): // #c0c0c0
513 return "silver";
514
515 case (128UL << 0) | (128UL << 8) | (128UL << 16) | (255UL << 24): // #808080
516 return "gray";
517
518 case (128UL << 0) | ( 0UL << 8) | ( 0UL << 16) | (255UL << 24): // #800000
519 return "maroon";
520
521 case (255UL << 0) | ( 0UL << 8) | ( 0UL << 16) | (255UL << 24): // #ff0000
522 return "red";
523
524 case (128UL << 0) | ( 0UL << 8) | (128UL << 16) | (255UL << 24): // #800080
525 return "purple";
526
527 case ( 0UL << 0) | (128UL << 8) | ( 0UL << 16) | (255UL << 24): // #008000
528 return "green";
529
530 case ( 0UL << 0) | (255UL << 8) | ( 0UL << 16) | (255UL << 24): // #00ff00
531 return "lime"; // == #0f0
532
533 case (128UL << 0) | (128UL << 8) | ( 0UL << 16) | (255UL << 24): // #808000
534 return "olive";
535
536 case ( 0UL << 0) | ( 0UL << 8) | (128UL << 16) | (255UL << 24): // #000080
537 return "navy";
538
539 case ( 0UL << 0) | ( 0UL << 8) | (255UL << 16) | (255UL << 24): // #0000ff
540 return "blue"; // == #00f
541
542 case ( 0UL << 0) | (128UL << 8) | (128UL << 16) | (255UL << 24): // #008080
543 return "teal";
544
545 case ( 0UL << 0) | (255UL << 8) | (255UL << 16) | (255UL << 24): // #00ffff
546 return "aqua"; // == #0ff
547
548 }
549
550 // output #RGB or #RRGGBB
551 std::stringstream ss;
552 ss << std::hex << "#";
553
554 if(((red >> 4) == (red & 15))
555 && ((green >> 4) == (green & 15))
556 && ((blue >> 4) == (blue & 15)))
557 {
558 // we can use the smaller format (#RGB)
559 ss << static_cast<int>(red & 15) << static_cast<int>(green & 15) << static_cast<int>(blue & 15);
560 return ss.str();
561 }
562
563 // cannot simplify (#RRGGBB)
564 ss << std::setfill('0')
565 << std::setw(2) << static_cast<int>(red)
566 << std::setw(2) << static_cast<int>(green)
567 << std::setw(2) << static_cast<int>(blue);
568 return ss.str();
569 }
570 else
571 {
572 if(alpha == 0) // is_transparent()
573 {
574 return "transparent"; // rgba(0,0,0,0)
575 }
576
577 // when alpha is specified we have to use the rgba() function
578 safe_precision_t safe(2);
579 return "rgba(" + std::to_string(static_cast<int>(red))
580 + "," + std::to_string(static_cast<int>(green))
581 + "," + std::to_string(static_cast<int>(blue))
582 + "," + decimal_number_to_string(f_alpha, true) + ")";
583 }
584}
585
586} // namespace csspp
587
588// Local Variables:
589// mode: cpp
590// indent-tabs-mode: nil
591// c-basic-offset: 4
592// tab-width: 4
593// End:
594
595// vim: ts=4 sw=4 et
rgba_color_t get_color() const
Definition color.cpp:467
color_component_t f_blue
Definition color.h:66
bool is_solid() const
Definition color.cpp:485
void set_color(rgba_color_t const rgba)
Definition color.cpp:228
color_component_t f_alpha
Definition color.h:67
std::string to_string() const
Definition color.cpp:495
void set_hsl(color_component_t h, color_component_t s, color_component_t l, color_component_t alpha)
Definition color.cpp:354
bool is_transparent() const
Definition color.cpp:490
void get_hsl(color_component_t &hue, color_component_t &saturation, color_component_t &lightness, color_component_t &alpha) const
Definition color.cpp:415
color_component_t f_red
Definition color.h:64
color_component_t f_green
Definition color.h:65
static bool constexpr is_hex(wide_char_t c)
Definition lexer.h:92
static int hex_to_dec(wide_char_t c)
Definition lexer.cpp:745
#define M_PI
Definition csspp.h:44
byte_component_t color_component_to_byte(color_component_t c)
Definition color.cpp:210
size_t const color_names_size(sizeof(color_names)/sizeof(color_names[0]))
color_table_t const color_names[]
Definition color.cpp:56
The namespace of all the classes in the CSS Preprocessor.
Definition csspp.h:48
std::string decimal_number_to_string(decimal_number_t d, bool remove_leading_zero)
Definition csspp.cpp:86
uint32_t rgba_color_t
Definition color.h:29
uint8_t byte_component_t
Definition color.h:28
float color_component_t
Definition color.h:27

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.