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

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.