]> Pileus Git - ~andy/gtk/blob - gtk/gtkgradient.c
b3ba261693f07dc132ca2c049b24b652066bb18f
[~andy/gtk] / gtk / gtkgradient.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 "gtkgradient.h"
22 #include "gtkstylecontextprivate.h"
23 #include "gtkstyleproperties.h"
24 #include "gtkintl.h"
25
26 /**
27  * SECTION:gtkgradient
28  * @Short_description: Gradients
29  * @Title: GtkGradient
30  *
31  * GtkGradient is a boxed type that represents a gradient.
32  * It is the result of parsing a
33  * <link linkend="gtkcssprovider-gradients">gradient expression</link>.
34  * To obtain the gradient represented by a GtkGradient, it has to
35  * be resolved with gtk_gradient_resolve(), which replaces all
36  * symbolic color references by the colors they refer to (in a given
37  * context) and constructs a #cairo_pattern_t value.
38  *
39  * It is not normally necessary to deal directly with #GtkGradients,
40  * since they are mostly used behind the scenes by #GtkStyleContext and
41  * #GtkCssProvider.
42  */
43
44 G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
45                      gtk_gradient_ref, gtk_gradient_unref)
46
47 typedef struct ColorStop ColorStop;
48
49 struct ColorStop
50 {
51   gdouble offset;
52   GtkSymbolicColor *color;
53 };
54
55 struct _GtkGradient
56 {
57   gdouble x0;
58   gdouble y0;
59   gdouble x1;
60   gdouble y1;
61   gdouble radius0;
62   gdouble radius1;
63
64   GArray *stops;
65
66   guint ref_count;
67 };
68
69 /**
70  * gtk_gradient_new_linear:
71  * @x0: X coordinate of the starting point
72  * @y0: Y coordinate of the starting point
73  * @x1: X coordinate of the end point
74  * @y1: Y coordinate of the end point
75  *
76  * Creates a new linear gradient along the line defined by (x0, y0) and (x1, y1). Before using the gradient
77  * a number of stop colors must be added through gtk_gradient_add_color_stop().
78  *
79  * Returns: A newly created #GtkGradient
80  *
81  * Since: 3.0
82  **/
83 GtkGradient *
84 gtk_gradient_new_linear (gdouble x0,
85                          gdouble y0,
86                          gdouble x1,
87                          gdouble y1)
88 {
89   GtkGradient *gradient;
90
91   gradient = g_slice_new (GtkGradient);
92   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
93
94   gradient->x0 = x0;
95   gradient->y0 = y0;
96   gradient->x1 = x1;
97   gradient->y1 = y1;
98   gradient->radius0 = 0;
99   gradient->radius1 = 0;
100
101   gradient->ref_count = 1;
102
103   return gradient;
104 }
105
106 /**
107  * gtk_gradient_new_radial:
108  * @x0: X coordinate of the start circle
109  * @y0: Y coordinate of the start circle
110  * @radius0: radius of the start circle
111  * @x1: X coordinate of the end circle
112  * @y1: Y coordinate of the end circle
113  * @radius1: radius of the end circle
114  *
115  * Creates a new radial gradient along the two circles defined by (x0, y0, radius0) and
116  * (x1, y1, radius1). Before using the gradient a number of stop colors must be added
117  * through gtk_gradient_add_color_stop().
118  *
119  * Returns: A newly created #GtkGradient
120  *
121  * Since: 3.0
122  **/
123 GtkGradient *
124 gtk_gradient_new_radial (gdouble x0,
125                          gdouble y0,
126                          gdouble radius0,
127                          gdouble x1,
128                          gdouble y1,
129                          gdouble radius1)
130 {
131   GtkGradient *gradient;
132
133   gradient = g_slice_new (GtkGradient);
134   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
135
136   gradient->x0 = x0;
137   gradient->y0 = y0;
138   gradient->x1 = x1;
139   gradient->y1 = y1;
140   gradient->radius0 = radius0;
141   gradient->radius1 = radius1;
142
143   gradient->ref_count = 1;
144
145   return gradient;
146 }
147
148 /**
149  * gtk_gradient_add_color_stop:
150  * @gradient: a #GtkGradient
151  * @offset: offset for the color stop
152  * @color: color to use
153  *
154  * Adds a stop color to @gradient.
155  *
156  * Since: 3.0
157  **/
158 void
159 gtk_gradient_add_color_stop (GtkGradient      *gradient,
160                              gdouble           offset,
161                              GtkSymbolicColor *color)
162 {
163   ColorStop stop;
164
165   g_return_if_fail (gradient != NULL);
166
167   stop.offset = offset;
168   stop.color = gtk_symbolic_color_ref (color);
169
170   g_array_append_val (gradient->stops, stop);
171 }
172
173 /**
174  * gtk_gradient_ref:
175  * @gradient: a #GtkGradient
176  *
177  * Increases the reference count of @gradient.
178  *
179  * Returns: The same @gradient
180  *
181  * Since: 3.0
182  **/
183 GtkGradient *
184 gtk_gradient_ref (GtkGradient *gradient)
185 {
186   g_return_val_if_fail (gradient != NULL, NULL);
187
188   gradient->ref_count++;
189
190   return gradient;
191 }
192
193 /**
194  * gtk_gradient_unref:
195  * @gradient: a #GtkGradient
196  *
197  * Decreases the reference count of @gradient, freeing its memory
198  * if the reference count reaches 0.
199  *
200  * Since: 3.0
201  **/
202 void
203 gtk_gradient_unref (GtkGradient *gradient)
204 {
205   g_return_if_fail (gradient != NULL);
206
207   gradient->ref_count--;
208
209   if (gradient->ref_count == 0)
210     {
211       guint i;
212
213       for (i = 0; i < gradient->stops->len; i++)
214         {
215           ColorStop *stop;
216
217           stop = &g_array_index (gradient->stops, ColorStop, i);
218           gtk_symbolic_color_unref (stop->color);
219         }
220
221       g_array_free (gradient->stops, TRUE);
222       g_slice_free (GtkGradient, gradient);
223     }
224 }
225
226 /**
227  * gtk_gradient_resolve:
228  * @gradient: a #GtkGradient
229  * @props: #GtkStyleProperties to use when resolving named colors
230  * @resolved_gradient: (out): return location for the resolved pattern
231  *
232  * If @gradient is resolvable, @resolved_gradient will be filled in
233  * with the resolved gradient as a cairo_pattern_t, and %TRUE will
234  * be returned. Generally, if @gradient can't be resolved, it is
235  * due to it being defined on top of a named color that doesn't
236  * exist in @props.
237  *
238  * Returns: %TRUE if the gradient has been resolved
239  *
240  * Since: 3.0
241  **/
242 gboolean
243 gtk_gradient_resolve (GtkGradient         *gradient,
244                       GtkStyleProperties  *props,
245                       cairo_pattern_t    **resolved_gradient)
246 {
247   cairo_pattern_t *pattern;
248   guint i;
249
250   g_return_val_if_fail (gradient != NULL, FALSE);
251   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
252   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
253
254   if (gradient->radius0 == 0 && gradient->radius1 == 0)
255     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
256                                            gradient->x1, gradient->y1);
257   else
258     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
259                                            gradient->radius0,
260                                            gradient->x1, gradient->y1,
261                                            gradient->radius1);
262
263   for (i = 0; i < gradient->stops->len; i++)
264     {
265       ColorStop *stop;
266       GdkRGBA color;
267
268       stop = &g_array_index (gradient->stops, ColorStop, i);
269
270       if (!gtk_symbolic_color_resolve (stop->color, props, &color))
271         {
272           cairo_pattern_destroy (pattern);
273           return FALSE;
274         }
275
276       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
277                                          color.red, color.green,
278                                          color.blue, color.alpha);
279     }
280
281   *resolved_gradient = pattern;
282   return TRUE;
283 }
284
285 cairo_pattern_t *
286 gtk_gradient_resolve_for_context (GtkGradient     *gradient,
287                                   GtkStyleContext *context)
288 {
289   cairo_pattern_t *pattern;
290   guint i;
291
292   g_return_val_if_fail (gradient != NULL, FALSE);
293   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
294
295   if (gradient->radius0 == 0 && gradient->radius1 == 0)
296     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
297                                            gradient->x1, gradient->y1);
298   else
299     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
300                                            gradient->radius0,
301                                            gradient->x1, gradient->y1,
302                                            gradient->radius1);
303
304   for (i = 0; i < gradient->stops->len; i++)
305     {
306       ColorStop *stop;
307       GdkRGBA rgba;
308
309       stop = &g_array_index (gradient->stops, ColorStop, i);
310
311       /* if color resolving fails, assume transparency */
312       if (!_gtk_style_context_resolve_color (context, stop->color, &rgba))
313         rgba.red = rgba.green = rgba.blue = rgba.alpha = 0.0;
314
315       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
316                                          rgba.red, rgba.green,
317                                          rgba.blue, rgba.alpha);
318     }
319
320   return pattern;
321 }
322
323 static void
324 append_number (GString    *str,
325                double      d,
326                const char *zero,
327                const char *half,
328                const char *one)
329 {
330   if (zero && d == 0.0)
331     g_string_append (str, zero);
332   else if (half && d == 0.5)
333     g_string_append (str, half);
334   else if (one && d == 1.0)
335     g_string_append (str, one);
336   else
337     {
338       char buf[G_ASCII_DTOSTR_BUF_SIZE];
339
340       g_ascii_dtostr (buf, sizeof (buf), d);
341       g_string_append (str, buf);
342     }
343 }
344
345 /**
346  * gtk_gradient_to_string:
347  * @gradient: the gradient to print
348  *
349  * Creates a string representation for @gradient that is suitable
350  * for using in GTK CSS files.
351  *
352  * Returns: A string representation for @gradient
353  **/
354 char *
355 gtk_gradient_to_string (GtkGradient *gradient)
356 {
357   GString *str;
358   guint i;
359
360   g_return_val_if_fail (gradient != NULL, NULL);
361
362   str = g_string_new ("-gtk-gradient (");
363
364   if (gradient->radius0 == 0 && gradient->radius1 == 0)
365     {
366       g_string_append (str, "linear, ");
367       append_number (str, gradient->x0, "left", "center", "right");
368       g_string_append_c (str, ' ');
369       append_number (str, gradient->y0, "top", "center", "bottom");
370       g_string_append (str, ", ");
371       append_number (str, gradient->x1, "left", "center", "right");
372       g_string_append_c (str, ' ');
373       append_number (str, gradient->y1, "top", "center", "bottom");
374     }
375   else
376     {
377       g_string_append (str, "radial, ");
378       append_number (str, gradient->x0, "left", "center", "right");
379       g_string_append_c (str, ' ');
380       append_number (str, gradient->y0, "top", "center", "bottom");
381       g_string_append (str, ", ");
382       append_number (str, gradient->radius0, NULL, NULL, NULL);
383       g_string_append (str, ", ");
384       append_number (str, gradient->x1, "left", "center", "right");
385       g_string_append_c (str, ' ');
386       append_number (str, gradient->y1, "top", "center", "bottom");
387       g_string_append (str, ", ");
388       append_number (str, gradient->radius1, NULL, NULL, NULL);
389     }
390   
391   for (i = 0; i < gradient->stops->len; i++)
392     {
393       ColorStop *stop;
394       char *s;
395
396       stop = &g_array_index (gradient->stops, ColorStop, i);
397
398       g_string_append (str, ", ");
399
400       if (stop->offset == 0.0)
401         g_string_append (str, "from (");
402       else if (stop->offset == 1.0)
403         g_string_append (str, "to (");
404       else
405         {
406           g_string_append (str, "color-stop (");
407           append_number (str, stop->offset, NULL, NULL, NULL);
408           g_string_append (str, ", ");
409         }
410
411       s = gtk_symbolic_color_to_string (stop->color);
412       g_string_append (str, s);
413       g_free (s);
414
415       g_string_append (str, ")");
416     }
417
418   g_string_append (str, ")");
419
420   return g_string_free (str, FALSE);
421 }