1 /* This file is part of GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL 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 GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2006 Martin Nordholts <enselic@hotmail.com>
17  */
18 
19 #include "config.h"
20 
21 #include <string.h>
22 
23 #include <glib-object.h>
24 
25 #include "gegl.h"
26 #include "gegl-types-internal.h"
27 #include "gegl-color.h"
28 
29 
30 enum
31 {
32   PROP_0,
33   PROP_STRING
34 };
35 
36 typedef struct _ColorNameEntity ColorNameEntity;
37 
38 struct _GeglColorPrivate
39 {
40   const Babl *format;
41 
42   union
43   {
44     guint8  pixel[40];
45     gdouble alignment;
46   };
47 };
48 
49 struct _ColorNameEntity
50 {
51   const gchar *color_name;
52   const gfloat rgba_color[4];
53 };
54 
55 static gboolean  parse_float_argument_list (float rgba_color[4],
56                                             GScanner  *scanner,
57                                             gint       num_arguments);
58 static gboolean  parse_color_name (float rgba_color[4],
59                                    const gchar *color_string);
60 static gboolean  parse_hex (float rgba_color[4],
61                             const gchar *color_string);
62 static void      set_property (GObject      *gobject,
63                                guint         prop_id,
64                                const GValue *value,
65                                GParamSpec   *pspec);
66 static void      get_property (GObject    *gobject,
67                                guint       prop_id,
68                                GValue     *value,
69                                GParamSpec *pspec);
70 
71 /* These color names are based on those defined in the HTML 4.01 standard. See
72  * http://www.w3.org/TR/html4/types.html#h-6.5
73  *
74  * Note: these values are stored with gamma
75  */
76 static const ColorNameEntity color_names[] =
77 {
78   { "black",   { 0.f,      0.f,      0.f,      1.f } },
79   { "silver",  { 0.75294f, 0.75294f, 0.75294f, 1.f } },
80   { "gray",    { 0.50196f, 0.50196f, 0.50196f, 1.f } },
81   { "white",   { 1.f,      1.f,      1.f,      1.f } },
82   { "maroon",  { 0.50196f, 0.f,      0.f,      1.f } },
83   { "red",     { 1.f,      0.f,      0.f,      1.f } },
84   { "purple",  { 0.50196f, 0.f,      0.50196f, 1.f } },
85   { "fuchsia", { 1.f,      0.f,      1.f,      1.f } },
86   { "green",   { 0.f,      0.50196f, 0.f,      1.f } },
87   { "lime",    { 0.f,      1.f,      0.f,      1.f } },
88   { "olive",   { 0.50196f, 0.50196f, 0.f,      1.f } },
89   { "yellow",  { 1.f,      1.f,      0.f,      1.f } },
90   { "navy",    { 0.f,      0.f,      0.50196f, 1.f } },
91   { "blue",    { 0.f,      0.f,      1.f,      1.f } },
92   { "teal",    { 0.f,      0.50196f, 0.50196f, 1.f } },
93   { "aqua",    { 0.f,      1.f,      1.f,      1.f } },
94   { "none",    { 0.f,      0.f,      0.f,      0.f } },
95   { "transparent",  { 0.f, 0.f,      0.f,      0.f } }
96 };
97 
98 /* Copied into GeglColor:s instances when parsing a color from a string fails. */
99 static const gfloat parsing_error_color[4] = { 0.f, 1.f, 1.f, 0.67f };
100 
101 /* Copied into all GeglColor:s at their instantiation. */
102 static const gfloat init_color[4] = { 1.f, 1.f, 1.f, 1.f };
103 
G_DEFINE_TYPE_WITH_PRIVATE(GeglColor,gegl_color,G_TYPE_OBJECT)104 G_DEFINE_TYPE_WITH_PRIVATE (GeglColor, gegl_color, G_TYPE_OBJECT)
105 
106 
107 static void
108 gegl_color_init (GeglColor *self)
109 {
110   self->priv = gegl_color_get_instance_private ((self));
111 
112   self->priv->format = gegl_babl_rgba_linear_float ();
113 
114   memcpy (self->priv->pixel, init_color, sizeof (init_color));
115 }
116 
117 static void
gegl_color_class_init(GeglColorClass * klass)118 gegl_color_class_init (GeglColorClass *klass)
119 {
120   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
121 
122   gobject_class->set_property = set_property;
123   gobject_class->get_property = get_property;
124 
125   g_object_class_install_property (gobject_class, PROP_STRING,
126                                    g_param_spec_string ("string",
127                                                         "String",
128                                                         "A String representation of the GeglColor",
129                                                         "",
130                                                         G_PARAM_READWRITE |
131                                                         G_PARAM_STATIC_STRINGS));
132 }
133 
134 static gboolean
parse_float_argument_list(float rgba_color[4],GScanner * scanner,gint num_arguments)135 parse_float_argument_list (float rgba_color[4],
136                            GScanner  *scanner,
137                            gint       num_arguments)
138 {
139   GTokenType        token_type;
140   GTokenValue       token_value;
141   gint              i;
142 
143   /* Make sure there is a leading '(' */
144   if (g_scanner_get_next_token (scanner) != G_TOKEN_LEFT_PAREN)
145     {
146       return FALSE;
147     }
148 
149   /* Iterate through the arguments and copy each value
150    * to the rgba_color array of GeglColor.
151    */
152   for (i = 0; i < num_arguments; ++i)
153     {
154       switch (g_scanner_get_next_token (scanner))
155         {
156           case G_TOKEN_FLOAT:
157             token_value = g_scanner_cur_value (scanner);
158             rgba_color[i] = token_value.v_float;
159             break;
160 
161           case G_TOKEN_INT:
162             token_value = g_scanner_cur_value (scanner);
163             rgba_color[i] = token_value.v_int64;
164             break;
165 
166           default:
167             return FALSE;
168         }
169 
170       /* Verify that there is a ',' after each float, except the last one */
171       if (i < (num_arguments - 1))
172         {
173           token_type = g_scanner_get_next_token (scanner);
174           if (token_type != G_TOKEN_COMMA)
175             {
176               return FALSE;
177             }
178         }
179     }
180 
181   /* Make sure there is a trailing ')' and that that is the last token. */
182   if (g_scanner_get_next_token (scanner) == G_TOKEN_RIGHT_PAREN &&
183       g_scanner_get_next_token (scanner) == G_TOKEN_EOF)
184     {
185       return TRUE;
186     }
187 
188   return FALSE;
189 }
190 
191 static gboolean
parse_color_name(float rgba_color[4],const gchar * color_string)192 parse_color_name (float rgba_color[4],
193                   const gchar *color_string)
194 {
195   gsize i;
196 
197   for (i = 0; i < G_N_ELEMENTS (color_names); ++i)
198     {
199       if (g_ascii_strcasecmp (color_names[i].color_name, color_string) == 0)
200         {
201           memcpy (rgba_color, color_names[i].rgba_color, sizeof (color_names[i].rgba_color));
202           return TRUE;
203         }
204     }
205 
206   return FALSE;
207 }
208 
209 static gboolean
parse_hex(float rgba_color[4],const gchar * color_string)210 parse_hex (float rgba_color[4],
211            const gchar *color_string)
212 {
213   gint              i;
214   gsize             string_length = strlen (color_string);
215 
216   if (string_length == 7 ||  /* #rrggbb   */
217       string_length == 9)    /* #rrggbbaa */
218     {
219       gint num_iterations = (string_length - 1) / 2;
220       for (i = 0; i < num_iterations; ++i)
221         {
222           if (g_ascii_isxdigit (color_string[2 * i + 1]) &&
223               g_ascii_isxdigit (color_string[2 * i + 2]))
224             {
225               rgba_color[i] = (g_ascii_xdigit_value (color_string[2 * i + 1]) << 4 |
226                                      g_ascii_xdigit_value (color_string[2 * i + 2])) / 255.f;
227             }
228           else
229             {
230               return FALSE;
231             }
232         }
233 
234       /* Successful #rrggbb(aa) parsing! */
235       return TRUE;
236     }
237   else if (string_length == 4 ||  /* #rgb  */
238            string_length == 5)    /* #rgba */
239     {
240       gint num_iterations = string_length - 1;
241       for (i = 0; i < num_iterations; ++i)
242         {
243           if (g_ascii_isxdigit (color_string[i + 1]))
244             {
245               rgba_color[i] = (g_ascii_xdigit_value (color_string[i + 1]) << 4 |
246                                      g_ascii_xdigit_value (color_string[i + 1])) / 255.f;
247             }
248           else
249             {
250               return FALSE;
251             }
252         }
253 
254       /* Successful #rgb(a) parsing! */
255       return TRUE;
256     }
257 
258   /* String was of unsupported length. */
259   return FALSE;
260 }
261 
262 #if 0
263 const gfloat *
264 gegl_color_float4 (GeglColor *self)
265 {
266   g_return_val_if_fail (GEGL_IS_COLOR (self), NULL);
267   return &self->rgba_color[0];
268 }
269 #endif
270 
271 void
gegl_color_set_pixel(GeglColor * color,const Babl * format,const void * pixel)272 gegl_color_set_pixel (GeglColor   *color,
273                       const Babl  *format,
274                       const void  *pixel)
275 {
276   gint bpp;
277 
278   g_return_if_fail (GEGL_IS_COLOR (color));
279   g_return_if_fail (format);
280   g_return_if_fail (pixel);
281 
282   bpp = babl_format_get_bytes_per_pixel (format);
283 
284   if (bpp <= sizeof (color->priv->pixel))
285     color->priv->format = format;
286   else
287     color->priv->format = gegl_babl_rgba_linear_float ();
288 
289   babl_process (babl_fish (format, color->priv->format),
290                 pixel, color->priv->pixel, 1);
291 }
292 
293 const Babl *
gegl_color_get_format(GeglColor * color)294 gegl_color_get_format (GeglColor *color)
295 {
296   g_return_val_if_fail (GEGL_IS_COLOR (color), NULL);
297   return color->priv->format;
298 }
299 
300 void
gegl_color_get_pixel(GeglColor * color,const Babl * format,void * pixel)301 gegl_color_get_pixel (GeglColor   *color,
302                       const Babl  *format,
303                       void        *pixel)
304 {
305   g_return_if_fail (GEGL_IS_COLOR (color));
306   g_return_if_fail (format);
307   g_return_if_fail (pixel);
308 
309   babl_process (babl_fish (color->priv->format, format),
310                 color->priv->pixel, pixel, 1);
311 }
312 
313 void
gegl_color_set_rgba(GeglColor * self,gdouble r,gdouble g,gdouble b,gdouble a)314 gegl_color_set_rgba (GeglColor *self,
315                      gdouble    r,
316                      gdouble    g,
317                      gdouble    b,
318                      gdouble    a)
319 {
320   const gfloat rgba[4] = {r, g, b, a};
321 
322   g_return_if_fail (GEGL_IS_COLOR (self));
323 
324   gegl_color_set_pixel (self, gegl_babl_rgba_linear_float (), rgba);
325 }
326 
327 void
gegl_color_get_rgba(GeglColor * self,gdouble * r,gdouble * g,gdouble * b,gdouble * a)328 gegl_color_get_rgba (GeglColor *self,
329                      gdouble   *r,
330                      gdouble   *g,
331                      gdouble   *b,
332                      gdouble   *a)
333 {
334   gfloat rgba[4];
335 
336   g_return_if_fail (GEGL_IS_COLOR (self));
337 
338   gegl_color_get_pixel (self, gegl_babl_rgba_linear_float (), rgba);
339 
340   if (r) *r = rgba[0];
341   if (g) *g = rgba[1];
342   if (b) *b = rgba[2];
343   if (a) *a = rgba[3];
344 }
345 
346 static void
gegl_color_set_from_string(GeglColor * self,const gchar * color_string)347 gegl_color_set_from_string (GeglColor   *self,
348                             const gchar *color_string)
349 {
350   GScanner         *scanner;
351   GTokenType        token_type;
352   GTokenValue       token_value;
353   gboolean          color_parsing_successfull;
354   float cmyka[5] = {0.0, 0.0, 0.0, 1.0, 1.0};
355   const Babl *format = gegl_babl_rgba_float ();
356   float *rgba=&cmyka[0];
357 
358   scanner                               = g_scanner_new (NULL);
359   scanner->config->cpair_comment_single = "";
360   g_scanner_input_text (scanner, color_string, strlen (color_string));
361 
362   token_type  = g_scanner_get_next_token (scanner);
363   token_value = g_scanner_cur_value (scanner);
364 
365   if (token_type == G_TOKEN_IDENTIFIER &&
366       g_ascii_strcasecmp (token_value.v_identifier, "cmyk") == 0)
367     {
368       color_parsing_successfull = parse_float_argument_list (cmyka, scanner, 4);
369       for (int i = 0; i<4;i++)
370         cmyka[i] /= 100.0;
371 
372       format = babl_format ("CMYK float");
373     }
374   else if (token_type == G_TOKEN_IDENTIFIER &&
375       g_ascii_strcasecmp (token_value.v_identifier, "cmyka") == 0)
376     {
377       color_parsing_successfull = parse_float_argument_list (cmyka, scanner, 5);
378       for (int i = 0; i<4;i++)
379         cmyka[i] /= 100.0;
380       format = babl_format ("CMYKA float");
381     }
382   else if (token_type == G_TOKEN_IDENTIFIER &&
383       g_ascii_strcasecmp (token_value.v_identifier, "rgb") == 0)
384     {
385       color_parsing_successfull = parse_float_argument_list (rgba, scanner, 3);
386       format = gegl_babl_rgba_linear_float ();
387     }
388   else if (token_type == G_TOKEN_IDENTIFIER &&
389            g_ascii_strcasecmp (token_value.v_identifier, "rgba") == 0)
390     {
391       rgba[3] = 1.0;
392       color_parsing_successfull = parse_float_argument_list (rgba, scanner, 4);
393       format = gegl_babl_rgba_linear_float ();
394     }
395   else if (token_type == '#')
396     {
397       color_parsing_successfull = parse_hex (rgba, color_string);
398     }
399   else if (token_type == G_TOKEN_IDENTIFIER)
400     {
401       color_parsing_successfull = parse_color_name (rgba, color_string);
402     }
403   else
404     {
405       color_parsing_successfull = FALSE;
406     }
407 
408   if (color_parsing_successfull)
409     {
410       gegl_color_set_pixel(self, format, cmyka);
411     }
412   else
413     {
414       gegl_color_set_pixel(self,
415                            gegl_babl_rgba_linear_float (),
416                            parsing_error_color);
417 
418       g_warning ("Parsing of color string \"%s\" into GeglColor failed! "
419                  "Using transparent cyan instead",
420                  color_string);
421     }
422 
423   g_scanner_destroy (scanner);
424 }
425 
426 static gchar *
gegl_color_get_string(GeglColor * color)427 gegl_color_get_string (GeglColor *color)
428 {
429   gfloat rgba[4];
430 
431   gegl_color_get_pixel (color, gegl_babl_rgba_linear_float (), rgba);
432 
433   if (babl_get_model_flags (color->priv->format) & BABL_MODEL_FLAG_CMYK)
434   {
435     gfloat cmyka[5];
436     gchar buf [5][G_ASCII_DTOSTR_BUF_SIZE];
437     gegl_color_get_pixel (color, babl_format ("CMYKA float"), cmyka);
438     g_ascii_formatd (buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%1.1f", cmyka[0]*100);
439     g_ascii_formatd (buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%1.1f", cmyka[1]*100);
440     g_ascii_formatd (buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%1.1f", cmyka[2]*100);
441     g_ascii_formatd (buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%1.1f", cmyka[3]*100);
442     g_ascii_formatd (buf[4], G_ASCII_DTOSTR_BUF_SIZE, "%1.1f", cmyka[3]);
443     if (cmyka[4] == 1.0)
444     {
445       return g_strdup_printf ("cmyk(%s, %s, %s, %s)", buf[0], buf[1], buf[2], buf[3]);
446     }
447     else
448     {
449       return g_strdup_printf ("cmyka(%s, %s, %s, %s, %s)", buf[0], buf[1], buf[2], buf[3], buf[4]);
450     }
451   }
452 
453   if (rgba[3] == 1.0)
454     {
455       gchar buf [3][G_ASCII_DTOSTR_BUF_SIZE];
456       g_ascii_formatd (buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[0]);
457       g_ascii_formatd (buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[1]);
458       g_ascii_formatd (buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[2]);
459       return g_strdup_printf ("rgb(%s, %s, %s)", buf[0], buf[1], buf[2]);
460     }
461   else
462     {
463       gchar buf [4][G_ASCII_DTOSTR_BUF_SIZE];
464       g_ascii_formatd (buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[0]);
465       g_ascii_formatd (buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[1]);
466       g_ascii_formatd (buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[2]);
467       g_ascii_formatd (buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%1.3f", rgba[3]);
468       return g_strdup_printf ("rgba(%s, %s, %s, %s)", buf[0], buf[1], buf[2], buf[3]);
469     }
470 }
471 
472 
473 static void
set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)474 set_property (GObject      *gobject,
475               guint         property_id,
476               const GValue *value,
477               GParamSpec   *pspec)
478 {
479   GeglColor *color = GEGL_COLOR (gobject);
480 
481   switch (property_id)
482     {
483       case PROP_STRING:
484         gegl_color_set_from_string (color, g_value_get_string (value));
485         break;
486 
487       default:
488         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
489         break;
490     }
491 }
492 
493 static void
get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)494 get_property (GObject    *gobject,
495               guint       property_id,
496               GValue     *value,
497               GParamSpec *pspec)
498 {
499   GeglColor *color = GEGL_COLOR (gobject);
500 
501   switch (property_id)
502     {
503       case PROP_STRING:
504       {
505         gchar *string = gegl_color_get_string (color);
506         g_value_set_string (value, string);
507         g_free (string);
508       }
509         break;
510 
511       default:
512         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
513         break;
514     }
515 }
516 
517 GeglColor *
gegl_color_new(const gchar * string)518 gegl_color_new (const gchar *string)
519 {
520   if (string)
521     return g_object_new (GEGL_TYPE_COLOR, "string", string, NULL);
522 
523   return g_object_new (GEGL_TYPE_COLOR, NULL);
524 }
525 
526 GeglColor *
gegl_color_duplicate(GeglColor * color)527 gegl_color_duplicate (GeglColor *color)
528 {
529   GeglColor *new;
530 
531   g_return_val_if_fail (GEGL_IS_COLOR (color), NULL);
532 
533   new = g_object_new (GEGL_TYPE_COLOR, NULL);
534 
535   memcpy (new->priv, color->priv, sizeof (GeglColorPrivate));
536 
537   return new;
538 }
539 
540 /* --------------------------------------------------------------------------
541  * A GParamSpec class to describe behavior of GeglColor as an object property
542  * follows.
543  * --------------------------------------------------------------------------
544  */
545 
546 #define GEGL_PARAM_COLOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_PARAM_COLOR, GeglParamColor))
547 #define GEGL_IS_PARAM_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_PARAM_COLOR))
548 
549 typedef struct _GeglParamColor GeglParamColor;
550 
551 struct _GeglParamColor
552 {
553   GParamSpec parent_instance;
554 
555   GeglColor *default_color;
556 };
557 
558 static void
gegl_param_color_init(GParamSpec * self)559 gegl_param_color_init (GParamSpec *self)
560 {
561   GEGL_PARAM_COLOR (self)->default_color = NULL;
562 }
563 
564 GeglColor *
gegl_param_spec_color_get_default(GParamSpec * self)565 gegl_param_spec_color_get_default (GParamSpec *self)
566 {
567   return GEGL_PARAM_COLOR (self)->default_color;
568 }
569 
570 static void
gegl_param_color_finalize(GParamSpec * self)571 gegl_param_color_finalize (GParamSpec *self)
572 {
573   GeglParamColor  *param_color  = GEGL_PARAM_COLOR (self);
574   GParamSpecClass *parent_class = g_type_class_peek (g_type_parent (GEGL_TYPE_PARAM_COLOR));
575 
576   g_clear_object (&param_color->default_color);
577 
578   parent_class->finalize (self);
579 }
580 
581 static void
gegl_param_color_set_default(GParamSpec * param_spec,GValue * value)582 gegl_param_color_set_default (GParamSpec *param_spec,
583                               GValue     *value)
584 {
585   GeglParamColor *gegl_color = GEGL_PARAM_COLOR (param_spec);
586 
587   if (gegl_color->default_color)
588     g_value_take_object (value, gegl_color_duplicate (gegl_color->default_color));
589 }
590 
591 GType
gegl_param_color_get_type(void)592 gegl_param_color_get_type (void)
593 {
594   static GType param_color_type = 0;
595 
596   if (G_UNLIKELY (param_color_type == 0))
597     {
598       static GParamSpecTypeInfo param_color_type_info = {
599         sizeof (GeglParamColor),
600         0,
601         gegl_param_color_init,
602         0,
603         gegl_param_color_finalize,
604         gegl_param_color_set_default,
605         NULL,
606         NULL
607       };
608       param_color_type_info.value_type = GEGL_TYPE_COLOR;
609 
610       param_color_type = g_param_type_register_static ("GeglParamColor",
611                                                        &param_color_type_info);
612     }
613 
614   return param_color_type;
615 }
616 
617 GParamSpec *
gegl_param_spec_color(const gchar * name,const gchar * nick,const gchar * blurb,GeglColor * default_color,GParamFlags flags)618 gegl_param_spec_color (const gchar *name,
619                        const gchar *nick,
620                        const gchar *blurb,
621                        GeglColor   *default_color,
622                        GParamFlags  flags)
623 {
624   GeglParamColor *param_color;
625 
626   param_color = g_param_spec_internal (GEGL_TYPE_PARAM_COLOR,
627                                        name, nick, blurb, flags);
628 
629   param_color->default_color = default_color;
630   if (default_color)
631     g_object_ref (default_color);
632 
633   return G_PARAM_SPEC (param_color);
634 }
635 
636 GParamSpec *
gegl_param_spec_color_from_string(const gchar * name,const gchar * nick,const gchar * blurb,const gchar * default_color_string,GParamFlags flags)637 gegl_param_spec_color_from_string (const gchar *name,
638                                    const gchar *nick,
639                                    const gchar *blurb,
640                                    const gchar *default_color_string,
641                                    GParamFlags  flags)
642 {
643   GeglParamColor *param_color;
644 
645   param_color = g_param_spec_internal (GEGL_TYPE_PARAM_COLOR,
646                                        name, nick, blurb, flags);
647 
648   param_color->default_color = g_object_new (GEGL_TYPE_COLOR,
649                                              "string", default_color_string,
650                                              NULL);
651 
652   return G_PARAM_SPEC (param_color);
653 }
654