]> Pileus Git - ~andy/gtk/blob - gtk/gtksymboliccolor.c
310e2113422abd35d88f6dfee1716664a80c9883
[~andy/gtk] / gtk / gtksymboliccolor.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gtksymboliccolor.h"
22 #include "gtkstyleset.h"
23 #include "gtkintl.h"
24
25 G_DEFINE_BOXED_TYPE (GtkSymbolicColor, gtk_symbolic_color,
26                      gtk_symbolic_color_ref, gtk_symbolic_color_unref)
27 G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
28                      gtk_gradient_ref, gtk_gradient_unref)
29
30 /* Symbolic colors */
31 typedef enum {
32   COLOR_TYPE_LITERAL,
33   COLOR_TYPE_NAME,
34   COLOR_TYPE_SHADE,
35   COLOR_TYPE_MIX
36 } ColorType;
37
38 struct GtkSymbolicColor
39 {
40   ColorType type;
41   guint ref_count;
42
43   union
44   {
45     GdkColor color;
46     gchar *name;
47
48     struct
49     {
50       GtkSymbolicColor *color;
51       gdouble factor;
52     } shade;
53
54     struct
55     {
56       GtkSymbolicColor *color1;
57       GtkSymbolicColor *color2;
58       gdouble factor;
59     } mix;
60   };
61 };
62
63 typedef struct ColorStop ColorStop;
64
65 struct ColorStop
66 {
67   gdouble offset;
68   GtkSymbolicColor *color;
69 };
70
71 struct GtkGradient
72 {
73   gdouble x0;
74   gdouble y0;
75   gdouble x1;
76   gdouble y1;
77
78   GArray *stops;
79
80   guint ref_count;
81 };
82
83 GtkSymbolicColor *
84 gtk_symbolic_color_new_literal (GdkColor *color)
85 {
86   GtkSymbolicColor *symbolic_color;
87
88   g_return_val_if_fail (color != NULL, NULL);
89
90   symbolic_color = g_slice_new0 (GtkSymbolicColor);
91   symbolic_color->type = COLOR_TYPE_LITERAL;
92   symbolic_color->color = *color;
93   symbolic_color->ref_count = 1;
94
95   return symbolic_color;
96 }
97
98 GtkSymbolicColor *
99 gtk_symbolic_color_new_name (const gchar *name)
100 {
101   GtkSymbolicColor *symbolic_color;
102
103   g_return_val_if_fail (name != NULL, NULL);
104
105   symbolic_color = g_slice_new0 (GtkSymbolicColor);
106   symbolic_color->type = COLOR_TYPE_NAME;
107   symbolic_color->name = g_strdup (name);
108   symbolic_color->ref_count = 1;
109
110   return symbolic_color;
111 }
112
113 GtkSymbolicColor *
114 gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
115                               gdouble           factor)
116 {
117   GtkSymbolicColor *symbolic_color;
118
119   g_return_val_if_fail (color != NULL, NULL);
120
121   symbolic_color = g_slice_new0 (GtkSymbolicColor);
122   symbolic_color->type = COLOR_TYPE_SHADE;
123   symbolic_color->shade.color = gtk_symbolic_color_ref (color);
124   symbolic_color->shade.factor = CLAMP (factor, 0, 1);
125   symbolic_color->ref_count = 1;
126
127   return symbolic_color;
128 }
129
130 GtkSymbolicColor *
131 gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
132                             GtkSymbolicColor *color2,
133                             gdouble           factor)
134 {
135   GtkSymbolicColor *symbolic_color;
136
137   g_return_val_if_fail (color1 != NULL, NULL);
138   g_return_val_if_fail (color1 != NULL, NULL);
139
140   symbolic_color = g_slice_new0 (GtkSymbolicColor);
141   symbolic_color->type = COLOR_TYPE_MIX;
142   symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
143   symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
144   symbolic_color->mix.factor = CLAMP (factor, 0, 1);
145   symbolic_color->ref_count = 1;
146
147   return symbolic_color;
148 }
149
150 GtkSymbolicColor *
151 gtk_symbolic_color_ref (GtkSymbolicColor *color)
152 {
153   g_return_val_if_fail (color != NULL, NULL);
154
155   color->ref_count++;
156
157   return color;
158 }
159
160 void
161 gtk_symbolic_color_unref (GtkSymbolicColor *color)
162 {
163   g_return_if_fail (color != NULL);
164
165   color->ref_count--;
166
167   if (color->ref_count == 0)
168     {
169       switch (color->type)
170         {
171         case COLOR_TYPE_NAME:
172           g_free (color->name);
173           break;
174         case COLOR_TYPE_SHADE:
175           gtk_symbolic_color_unref (color->shade.color);
176           break;
177         case COLOR_TYPE_MIX:
178           gtk_symbolic_color_unref (color->mix.color1);
179           gtk_symbolic_color_unref (color->mix.color2);
180           break;
181         default:
182           break;
183         }
184
185       g_slice_free (GtkSymbolicColor, color);
186     }
187 }
188
189 gboolean
190 gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
191                             GtkStyleSet         *style_set,
192                             GdkColor            *resolved_color)
193 {
194   g_return_val_if_fail (color != NULL, FALSE);
195   g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
196   g_return_val_if_fail (resolved_color != NULL, FALSE);
197
198   switch (color->type)
199     {
200     case COLOR_TYPE_LITERAL:
201       *resolved_color = color->color;
202       return TRUE;
203     case COLOR_TYPE_NAME:
204       {
205         GtkSymbolicColor *named_color;
206
207         named_color = gtk_style_set_lookup_color (style_set, color->name);
208
209         if (!named_color)
210           return FALSE;
211
212         return gtk_symbolic_color_resolve (named_color, style_set, resolved_color);
213       }
214
215       break;
216     case COLOR_TYPE_SHADE:
217       {
218         GdkColor shade;
219
220         if (!gtk_symbolic_color_resolve (color->shade.color, style_set, &shade))
221           return FALSE;
222
223         resolved_color->red = CLAMP (shade.red * color->shade.factor, 0, 65535);
224         resolved_color->green = CLAMP (shade.green * color->shade.factor, 0, 65535);
225         resolved_color->blue = CLAMP (shade.blue * color->shade.factor, 0, 65535);
226
227         return TRUE;
228       }
229
230       break;
231     case COLOR_TYPE_MIX:
232       {
233         GdkColor color1, color2;
234
235         if (!gtk_symbolic_color_resolve (color->mix.color1, style_set, &color1))
236           return FALSE;
237
238         if (!gtk_symbolic_color_resolve (color->mix.color2, style_set, &color2))
239           return FALSE;
240
241         resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 65535);
242         resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 65535);
243         resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 65535);
244
245         return TRUE;
246       }
247
248       break;
249     default:
250       g_assert_not_reached ();
251     }
252
253   return FALSE;
254 }
255
256 /* GtkGradient */
257 GtkGradient *
258 gtk_gradient_new_linear (gdouble x0,
259                          gdouble y0,
260                          gdouble x1,
261                          gdouble y1)
262 {
263   GtkGradient *gradient;
264
265   gradient = g_slice_new (GtkGradient);
266   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
267
268   gradient->x0 = x0;
269   gradient->y0 = y0;
270   gradient->x1 = x1;
271   gradient->y1 = y1;
272
273   gradient->ref_count = 1;
274
275   return gradient;
276 }
277
278 void
279 gtk_gradient_add_color_stop (GtkGradient      *gradient,
280                              gdouble           offset,
281                              GtkSymbolicColor *color)
282 {
283   ColorStop stop;
284
285   g_return_if_fail (gradient != NULL);
286
287   stop.offset = offset;
288   stop.color = gtk_symbolic_color_ref (color);
289
290   g_array_append_val (gradient->stops, stop);
291 }
292
293 GtkGradient *
294 gtk_gradient_ref (GtkGradient *gradient)
295 {
296   g_return_val_if_fail (gradient != NULL, NULL);
297
298   gradient->ref_count++;
299
300   return gradient;
301 }
302
303 void
304 gtk_gradient_unref (GtkGradient *gradient)
305 {
306   g_return_if_fail (gradient != NULL);
307
308   gradient->ref_count--;
309
310   if (gradient->ref_count == 0)
311     {
312       guint i;
313
314       for (i = 0; i < gradient->stops->len; i++)
315         {
316           ColorStop *stop;
317
318           stop = &g_array_index (gradient->stops, ColorStop, i);
319           gtk_symbolic_color_unref (stop->color);
320         }
321
322       g_array_free (gradient->stops, TRUE);
323       g_slice_free (GtkGradient, gradient);
324     }
325 }
326
327 gboolean
328 gtk_gradient_resolve (GtkGradient      *gradient,
329                       GtkStyleSet      *style_set,
330                       cairo_pattern_t **resolved_gradient)
331 {
332   cairo_pattern_t *pattern;
333   guint i;
334
335   g_return_val_if_fail (gradient != NULL, FALSE);
336   g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
337   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
338
339   pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
340                                          gradient->x1, gradient->y1);
341
342   for (i = 0; i < gradient->stops->len; i++)
343     {
344       ColorStop *stop;
345       GdkColor color;
346
347       stop = &g_array_index (gradient->stops, ColorStop, i);
348
349       if (!gtk_symbolic_color_resolve (stop->color, style_set, &color))
350         {
351           cairo_pattern_destroy (pattern);
352           return FALSE;
353         }
354
355       cairo_pattern_add_color_stop_rgb (pattern, stop->offset,
356                                         color.red / 65535.,
357                                         color.green / 65535.,
358                                         color.blue / 65535.);
359     }
360
361   *resolved_gradient = pattern;
362   return TRUE;
363 }