]> Pileus Git - ~andy/gtk/blob - gtk/gtksymboliccolor.c
Add GtkGradient.
[~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 (GtkGradient, gtk_gradient,
26                      gtk_gradient_ref, gtk_gradient_unref)
27
28 /* Symbolic colors */
29 typedef enum {
30   COLOR_TYPE_LITERAL,
31   COLOR_TYPE_NAME,
32   COLOR_TYPE_SHADE,
33   COLOR_TYPE_MIX
34 } ColorType;
35
36 struct GtkSymbolicColor
37 {
38   ColorType type;
39   guint ref_count;
40
41   union
42   {
43     GdkColor color;
44     gchar *name;
45
46     struct
47     {
48       GtkSymbolicColor *color;
49       gdouble factor;
50     } shade;
51
52     struct
53     {
54       GtkSymbolicColor *color1;
55       GtkSymbolicColor *color2;
56       gdouble factor;
57     } mix;
58   };
59 };
60
61 typedef struct ColorStop ColorStop;
62
63 struct ColorStop
64 {
65   gdouble offset;
66   GtkSymbolicColor *color;
67 };
68
69 struct GtkGradient
70 {
71   gdouble x0;
72   gdouble y0;
73   gdouble x1;
74   gdouble y1;
75
76   GArray *stops;
77
78   guint ref_count;
79 };
80
81 GtkSymbolicColor *
82 gtk_symbolic_color_new_literal (GdkColor *color)
83 {
84   GtkSymbolicColor *symbolic_color;
85
86   g_return_val_if_fail (color != NULL, NULL);
87
88   symbolic_color = g_slice_new0 (GtkSymbolicColor);
89   symbolic_color->type = COLOR_TYPE_LITERAL;
90   symbolic_color->color = *color;
91   symbolic_color->ref_count = 1;
92
93   return symbolic_color;
94 }
95
96 GtkSymbolicColor *
97 gtk_symbolic_color_new_name (const gchar *name)
98 {
99   GtkSymbolicColor *symbolic_color;
100
101   g_return_val_if_fail (name != NULL, NULL);
102
103   symbolic_color = g_slice_new0 (GtkSymbolicColor);
104   symbolic_color->type = COLOR_TYPE_NAME;
105   symbolic_color->name = g_strdup (name);
106   symbolic_color->ref_count = 1;
107
108   return symbolic_color;
109 }
110
111 GtkSymbolicColor *
112 gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
113                               gdouble           factor)
114 {
115   GtkSymbolicColor *symbolic_color;
116
117   g_return_val_if_fail (color != NULL, NULL);
118
119   symbolic_color = g_slice_new0 (GtkSymbolicColor);
120   symbolic_color->type = COLOR_TYPE_SHADE;
121   symbolic_color->shade.color = gtk_symbolic_color_ref (color);
122   symbolic_color->shade.factor = CLAMP (factor, 0, 1);
123   symbolic_color->ref_count = 1;
124
125   return symbolic_color;
126 }
127
128 GtkSymbolicColor *
129 gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
130                             GtkSymbolicColor *color2,
131                             gdouble           factor)
132 {
133   GtkSymbolicColor *symbolic_color;
134
135   g_return_val_if_fail (color1 != NULL, NULL);
136   g_return_val_if_fail (color1 != NULL, NULL);
137
138   symbolic_color = g_slice_new0 (GtkSymbolicColor);
139   symbolic_color->type = COLOR_TYPE_MIX;
140   symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
141   symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
142   symbolic_color->mix.factor = CLAMP (factor, 0, 1);
143   symbolic_color->ref_count = 1;
144
145   return symbolic_color;
146 }
147
148 GtkSymbolicColor *
149 gtk_symbolic_color_ref (GtkSymbolicColor *color)
150 {
151   g_return_val_if_fail (color != NULL, NULL);
152
153   color->ref_count++;
154
155   return color;
156 }
157
158 void
159 gtk_symbolic_color_unref (GtkSymbolicColor *color)
160 {
161   g_return_if_fail (color != NULL);
162
163   color->ref_count--;
164
165   if (color->ref_count == 0)
166     {
167       switch (color->type)
168         {
169         case COLOR_TYPE_NAME:
170           g_free (color->name);
171           break;
172         case COLOR_TYPE_SHADE:
173           gtk_symbolic_color_unref (color->shade.color);
174           break;
175         case COLOR_TYPE_MIX:
176           gtk_symbolic_color_unref (color->mix.color1);
177           gtk_symbolic_color_unref (color->mix.color2);
178           break;
179         default:
180           break;
181         }
182
183       g_slice_free (GtkSymbolicColor, color);
184     }
185 }
186
187 gboolean
188 gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
189                             GtkStyleSet         *style_set,
190                             GdkColor            *resolved_color)
191 {
192   g_return_val_if_fail (color != NULL, FALSE);
193   g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
194   g_return_val_if_fail (resolved_color != NULL, FALSE);
195
196   switch (color->type)
197     {
198     case COLOR_TYPE_LITERAL:
199       *resolved_color = color->color;
200       return TRUE;
201     case COLOR_TYPE_NAME:
202       {
203         GtkSymbolicColor *named_color;
204
205         named_color = gtk_style_set_lookup_color (style_set, color->name);
206
207         if (!named_color)
208           return FALSE;
209
210         return gtk_symbolic_color_resolve (named_color, style_set, resolved_color);
211       }
212
213       break;
214     case COLOR_TYPE_SHADE:
215       {
216         GdkColor shade;
217
218         if (!gtk_symbolic_color_resolve (color->shade.color, style_set, &shade))
219           return FALSE;
220
221         resolved_color->red = CLAMP (shade.red * color->shade.factor, 0, 65535);
222         resolved_color->green = CLAMP (shade.green * color->shade.factor, 0, 65535);
223         resolved_color->blue = CLAMP (shade.blue * color->shade.factor, 0, 65535);
224
225         return TRUE;
226       }
227
228       break;
229     case COLOR_TYPE_MIX:
230       {
231         GdkColor color1, color2;
232
233         if (!gtk_symbolic_color_resolve (color->mix.color1, style_set, &color1))
234           return FALSE;
235
236         if (!gtk_symbolic_color_resolve (color->mix.color2, style_set, &color2))
237           return FALSE;
238
239         resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 65535);
240         resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 65535);
241         resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 65535);
242
243         return TRUE;
244       }
245
246       break;
247     default:
248       g_assert_not_reached ();
249     }
250
251   return FALSE;
252 }
253
254 GType
255 gtk_symbolic_color_get_type (void)
256 {
257   static GType type = 0;
258
259   if (G_UNLIKELY (!type))
260     type = g_boxed_type_register_static (I_("GtkSymbolicColor"),
261                                          (GBoxedCopyFunc) gtk_symbolic_color_ref,
262                                          (GBoxedFreeFunc) gtk_symbolic_color_unref);
263
264   return type;
265 }
266
267 /* GtkGradient */
268 GtkGradient *
269 gtk_gradient_new_linear (gdouble x0,
270                          gdouble y0,
271                          gdouble x1,
272                          gdouble y1)
273 {
274   GtkGradient *gradient;
275
276   gradient = g_slice_new (GtkGradient);
277   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
278
279   gradient->x0 = x0;
280   gradient->y0 = y0;
281   gradient->x1 = x1;
282   gradient->y1 = y1;
283
284   gradient->ref_count = 1;
285
286   return gradient;
287 }
288
289 void
290 gtk_gradient_add_color_stop (GtkGradient      *gradient,
291                              gdouble           offset,
292                              GtkSymbolicColor *color)
293 {
294   ColorStop stop;
295
296   g_return_if_fail (gradient != NULL);
297
298   stop.offset = offset;
299   stop.color = gtk_symbolic_color_ref (color);
300
301   g_array_append_val (gradient->stops, stop);
302 }
303
304 GtkGradient *
305 gtk_gradient_ref (GtkGradient *gradient)
306 {
307   g_return_val_if_fail (gradient != NULL, NULL);
308
309   gradient->ref_count++;
310
311   return gradient;
312 }
313
314 void
315 gtk_gradient_unref (GtkGradient *gradient)
316 {
317   g_return_if_fail (gradient != NULL);
318
319   gradient->ref_count--;
320
321   if (gradient->ref_count == 0)
322     {
323       guint i;
324
325       for (i = 0; i < gradient->stops->len; i++)
326         {
327           ColorStop *stop;
328
329           stop = &g_array_index (gradient->stops, ColorStop, i);
330           gtk_symbolic_color_unref (stop->color);
331         }
332
333       g_array_free (gradient->stops, TRUE);
334       g_slice_free (GtkGradient, gradient);
335     }
336 }
337
338 gboolean
339 gtk_gradient_resolve (GtkGradient      *gradient,
340                       GtkStyleSet      *style_set,
341                       cairo_pattern_t **resolved_gradient)
342 {
343   cairo_pattern_t *pattern;
344   guint i;
345
346   g_return_val_if_fail (gradient != NULL, FALSE);
347   g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
348   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
349
350   pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
351                                          gradient->x1, gradient->y1);
352
353   for (i = 0; i < gradient->stops->len; i++)
354     {
355       ColorStop *stop;
356       GdkColor color;
357
358       stop = &g_array_index (gradient->stops, ColorStop, i);
359
360       if (!gtk_symbolic_color_resolve (stop->color, style_set, &color))
361         {
362           cairo_pattern_destroy (pattern);
363           return FALSE;
364         }
365
366       cairo_pattern_add_color_stop_rgb (pattern, stop->offset,
367                                         color.red / 65535.,
368                                         color.green / 65535.,
369                                         color.blue / 65535.);
370     }
371
372   *resolved_gradient = pattern;
373   return TRUE;
374 }