gdkrgba.c 9.43 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
17
18
19
20
21
22
23
24
25
26
27
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 */

#include "config.h"
#include "gdkrgba.h"
#include <string.h>
Matthias Clasen's avatar
Matthias Clasen committed
28
29
#include <errno.h>
#include <math.h>
30

31
32
#include "fallback-c89.c"

33
34
35
36
/**
 * SECTION:rgba_colors
 * @Short_description: RGBA colors
 * @Title: RGBA Colors
37
38
 *
 * The #GdkRGBA struct is a convenient way to pass rgba colors around.
39
 * It's based on cairo's way to deal with colors and mirrors its behavior.
40
41
42
43
 * All values are in the range from 0.0 to 1.0 inclusive. So the color
 * (0.0, 0.0, 0.0, 0.0) represents transparent black and
 * (1.0, 1.0, 1.0, 1.0) is opaque white. Other values will be clamped
 * to this range when drawing.
44
45
46
47
48
 */

G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
                     gdk_rgba_copy, gdk_rgba_free)

49
50
/**
 * GdkRGBA:
51
52
53
 * @red: The intensity of the red channel from 0.0 to 1.0 inclusive
 * @green: The intensity of the green channel from 0.0 to 1.0 inclusive
 * @blue: The intensity of the blue channel from 0.0 to 1.0 inclusive
54
 * @alpha: The opacity of the color from 0.0 for completely translucent to
55
 *   1.0 for opaque
56
 *
57
58
 * The GdkRGBA structure is used to represent a (possibly translucent)
 * color, in a way that is compatible with cairos notion of color.
59
60
 */

61
62
63
64
/**
 * gdk_rgba_copy:
 * @rgba: a #GdkRGBA
 *
65
 * Makes a copy of a #GdkRGBA structure.
66
 *
67
68
69
 * The result must be freed through gdk_rgba_free().
 *
 * Returns: A newly allocated #GdkRGBA, with the same contents as @rgba
70
71
 *
 * Since: 3.0
72
 */
73
GdkRGBA *
74
gdk_rgba_copy (const GdkRGBA *rgba)
75
{
Matthias Clasen's avatar
Matthias Clasen committed
76
  return g_slice_dup (GdkRGBA, rgba);
77
78
79
80
81
82
83
}

/**
 * gdk_rgba_free:
 * @rgba: a #GdkRGBA
 *
 * Frees a #GdkRGBA struct created with gdk_rgba_copy()
84
85
 *
 * Since: 3.0
86
 */
87
88
89
90
91
92
void
gdk_rgba_free (GdkRGBA *rgba)
{
  g_slice_free (GdkRGBA, rgba);
}

93
94
95
96
97
98
99
100
101
102
103
104
105
#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;

/* Parses a single color component from a rgb() or rgba() specification
 * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
 * in what we accept as follows:
 *
 *  - For non-percentage values, we accept floats in the range 0-255
 *    not just [0-9]+ integers
 *  - For percentage values we accept any float, not just
 *     [ 0-9]+ | [0-9]* '.' [0-9]+
 *  - We accept mixed percentages and non-percentages in a single
 *    rgb() or rgba() specification.
 */
Matthias Clasen's avatar
Matthias Clasen committed
106
107
108
109
static gboolean
parse_rgb_value (const gchar  *str,
                 gchar       **endp,
                 gdouble      *number)
110
111
112
{
  const char *p;

Matthias Clasen's avatar
Matthias Clasen committed
113
114
115
116
  *number = g_ascii_strtod (str, endp);
  if (errno == ERANGE || *endp == str ||
      isinf (*number) || isnan (*number))
    return FALSE;
117
118
119
120
121
122
123
124

  p = *endp;

  SKIP_WHITESPACES (p);

  if (*p == '%')
    {
      *endp = (char *)(p + 1);
Matthias Clasen's avatar
Matthias Clasen committed
125
      *number = CLAMP(*number / 100., 0., 1.);
126
127
128
    }
  else
    {
Matthias Clasen's avatar
Matthias Clasen committed
129
      *number = CLAMP(*number / 255., 0., 1.);
130
    }
Matthias Clasen's avatar
Matthias Clasen committed
131
132

  return TRUE;
133
134
}

135
136
137
/**
 * gdk_rgba_parse:
 * @rgba: the #GdkRGBA struct to fill in
138
 * @spec: the string specifying the color
139
140
141
142
143
144
145
146
147
148
149
150
 *
 * Parses a textual representation of a color, filling in
 * the <structfield>red</structfield>, <structfield>green</structfield>,
 * <structfield>blue</structfield> and <structfield>alpha</structfield>
 * fields of the @rgba struct.
 *
 * The string can be either one of:
 * <itemizedlist>
 * <listitem>
 * A standard name (Taken from the X11 rgb.txt file).
 * </listitem>
 * <listitem>
151
152
 * A hex value in the form '&num;rgb' '&num;rrggbb' '&num;rrrgggbbb'
 * or '&num;rrrrggggbbbb'
153
154
155
156
157
158
159
160
161
162
163
 * </listitem>
 * <listitem>
 * A RGB color in the form 'rgb(r,g,b)' (In this case the color will
 * have full opacity)
 * </listitem>
 * <listitem>
 * A RGBA color in the form 'rgba(r,g,b,a)'
 * </listitem>
 * </itemizedlist>
 *
 * Where 'r', 'g', 'b' and 'a' are respectively the red, green, blue and
164
165
166
 * alpha color values. In the last two cases, r g and b are either integers
 * in the range 0 to 255 or precentage values in the range 0% to 100%, and
 * a is a floating point value in the range 0 to 1.
167
168
 *
 * Returns: %TRUE if the parsing succeeded
169
170
 *
 * Since: 3.0
171
 */
172
gboolean
173
174
gdk_rgba_parse (GdkRGBA     *rgba,
                const gchar *spec)
175
176
177
178
{
  gboolean has_alpha;
  gdouble r, g, b, a;
  gchar *str = (gchar *) spec;
Matthias Clasen's avatar
Matthias Clasen committed
179
  gchar *p;
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

  if (strncmp (str, "rgba", 4) == 0)
    {
      has_alpha = TRUE;
      str += 4;
    }
  else if (strncmp (str, "rgb", 3) == 0)
    {
      has_alpha = FALSE;
      a = 1;
      str += 3;
    }
  else
    {
      PangoColor pango_color;

      /* Resort on PangoColor for rgb.txt color
197
198
       * map and '#' prefixed colors
       */
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
      if (pango_color_parse (&pango_color, str))
        {
          if (rgba)
            {
              rgba->red = pango_color.red / 65535.;
              rgba->green = pango_color.green / 65535.;
              rgba->blue = pango_color.blue / 65535.;
              rgba->alpha = 1;
            }

          return TRUE;
        }
      else
        return FALSE;
    }

  SKIP_WHITESPACES (str);

  if (*str != '(')
    return FALSE;

  str++;

  /* Parse red */
  SKIP_WHITESPACES (str);
Matthias Clasen's avatar
Matthias Clasen committed
224
225
  if (!parse_rgb_value (str, &str, &r))
    return FALSE;
226
227
228
229
230
231
232
233
234
  SKIP_WHITESPACES (str);

  if (*str != ',')
    return FALSE;

  str++;

  /* Parse green */
  SKIP_WHITESPACES (str);
Matthias Clasen's avatar
Matthias Clasen committed
235
236
  if (!parse_rgb_value (str, &str, &g))
    return FALSE;
237
238
239
240
241
242
243
244
245
  SKIP_WHITESPACES (str);

  if (*str != ',')
    return FALSE;

  str++;

  /* Parse blue */
  SKIP_WHITESPACES (str);
Matthias Clasen's avatar
Matthias Clasen committed
246
247
  if (!parse_rgb_value (str, &str, &b))
    return FALSE;
248
249
250
251
252
253
254
255
256
257
  SKIP_WHITESPACES (str);

  if (has_alpha)
    {
      if (*str != ',')
        return FALSE;

      str++;

      SKIP_WHITESPACES (str);
Matthias Clasen's avatar
Matthias Clasen committed
258
259
260
261
262
      a = g_ascii_strtod (str, &p);
      if (errno == ERANGE || p == str ||
          isinf (a) || isnan (a))
        return FALSE;
      str = p;
263
264
265
266
267
268
      SKIP_WHITESPACES (str);
    }

  if (*str != ')')
    return FALSE;

Matthias Clasen's avatar
Matthias Clasen committed
269
270
271
272
273
274
275
  str++;

  SKIP_WHITESPACES (str);

  if (*str != '\0')
    return FALSE;

276
277
278
279
280
281
282
283
284
285
286
  if (rgba)
    {
      rgba->red = CLAMP (r, 0, 1);
      rgba->green = CLAMP (g, 0, 1);
      rgba->blue = CLAMP (b, 0, 1);
      rgba->alpha = CLAMP (a, 0, 1);
    }

  return TRUE;
}

287
288
#undef SKIP_WHITESPACES

289
290
/**
 * gdk_rgba_hash:
291
 * @p: (type GdkRGBA): a #GdkRGBA pointer
292
293
 *
 * A hash function suitable for using for a hash
294
 * table that stores #GdkRGBAs.
295
 *
296
 * Return value: The hash value for @p
297
298
 *
 * Since: 3.0
299
 */
300
301
302
303
304
305
guint
gdk_rgba_hash (gconstpointer p)
{
  const GdkRGBA *rgba = p;

  return ((guint) (rgba->red * 65535) +
306
307
308
          ((guint) (rgba->green * 65535) << 11) +
          ((guint) (rgba->blue * 65535) << 22) +
          ((guint) (rgba->alpha * 65535) >> 6));
309
310
311
312
}

/**
 * gdk_rgba_equal:
313
314
 * @p1: (type GdkRGBA): a #GdkRGBA pointer
 * @p2: (type GdkRGBA): another #GdkRGBA pointer
315
316
317
318
 *
 * Compares two RGBA colors.
 *
 * Return value: %TRUE if the two colors compare equal
319
320
 *
 * Since: 3.0
321
 */
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
gboolean
gdk_rgba_equal (gconstpointer p1,
                gconstpointer p2)
{
  const GdkRGBA *rgba1, *rgba2;

  rgba1 = p1;
  rgba2 = p2;

  if (rgba1->red == rgba2->red &&
      rgba1->green == rgba2->green &&
      rgba1->blue == rgba2->blue &&
      rgba1->alpha == rgba2->alpha)
    return TRUE;

  return FALSE;
}

/**
 * gdk_rgba_to_string:
 * @rgba: a #GdkRGBA
 *
344
345
346
347
348
349
350
 * Returns a textual specification of @rgba in the form
 * <literal>rgb (r, g, b)</literal> or
 * <literal>rgba (r, g, b, a)</literal>,
 * where 'r', 'g', 'b' and 'a' represent the red, green,
 * blue and alpha values respectively. r, g, and b are
 * represented as integers in the range 0 to 255, and a
 * is represented as floating point value in the range 0 to 1.
351
 *
352
353
354
 * These string forms are string forms those supported by
 * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
 *
Bastien Nocera's avatar
Bastien Nocera committed
355
 * Note that this string representation may lose some
356
357
358
 * precision, since r, g and b are represented as 8-bit
 * integers. If this is a concern, you should use a
 * different representation.
359
360
 *
 * Returns: A newly allocated text string
361
362
 *
 * Since: 3.0
363
 */
364
gchar *
365
gdk_rgba_to_string (const GdkRGBA *rgba)
366
{
367
368
369
  if (rgba->alpha > 0.999)
    {
      return g_strdup_printf ("rgb(%d,%d,%d)",
370
371
372
                              (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
                              (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
                              (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
373
374
375
376
    }
  else
    {
      gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
377

378
      g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
379

380
      return g_strdup_printf ("rgba(%d,%d,%d,%s)",
381
382
383
384
                              (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
                              (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
                              (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
                              alpha);
385
    }
386
}