]> Pileus Git - ~andy/gtk/blob - gtk/gtkgradient.c
symboliccolor: Deprecate
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #define GDK_DISABLE_DEPRECATION_WARNINGS
21
22 #include "gtkgradientprivate.h"
23
24 #include "gtkcsscolorvalueprivate.h"
25 #include "gtkcssrgbavalueprivate.h"
26 #include "gtkstylecontextprivate.h"
27 #include "gtkstyleproperties.h"
28 #include "gtksymboliccolorprivate.h"
29
30 /**
31  * SECTION:gtkgradient
32  * @Short_description: Gradients
33  * @Title: GtkGradient
34  *
35  * GtkGradient is a boxed type that represents a gradient.
36  * It is the result of parsing a
37  * <link linkend="gtkcssprovider-gradients">gradient expression</link>.
38  * To obtain the gradient represented by a GtkGradient, it has to
39  * be resolved with gtk_gradient_resolve(), which replaces all
40  * symbolic color references by the colors they refer to (in a given
41  * context) and constructs a #cairo_pattern_t value.
42  *
43  * It is not normally necessary to deal directly with #GtkGradients,
44  * since they are mostly used behind the scenes by #GtkStyleContext and
45  * #GtkCssProvider.
46  */
47
48 G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
49                      gtk_gradient_ref, gtk_gradient_unref)
50
51 typedef struct ColorStop ColorStop;
52
53 struct ColorStop
54 {
55   gdouble offset;
56   GtkSymbolicColor *color;
57 };
58
59 struct _GtkGradient
60 {
61   gdouble x0;
62   gdouble y0;
63   gdouble x1;
64   gdouble y1;
65   gdouble radius0;
66   gdouble radius1;
67
68   GArray *stops;
69
70   guint ref_count;
71 };
72
73 /**
74  * gtk_gradient_new_linear:
75  * @x0: X coordinate of the starting point
76  * @y0: Y coordinate of the starting point
77  * @x1: X coordinate of the end point
78  * @y1: Y coordinate of the end point
79  *
80  * Creates a new linear gradient along the line defined by (x0, y0) and (x1, y1). Before using the gradient
81  * a number of stop colors must be added through gtk_gradient_add_color_stop().
82  *
83  * Returns: A newly created #GtkGradient
84  *
85  * Since: 3.0
86  **/
87 GtkGradient *
88 gtk_gradient_new_linear (gdouble x0,
89                          gdouble y0,
90                          gdouble x1,
91                          gdouble y1)
92 {
93   GtkGradient *gradient;
94
95   gradient = g_slice_new (GtkGradient);
96   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
97
98   gradient->x0 = x0;
99   gradient->y0 = y0;
100   gradient->x1 = x1;
101   gradient->y1 = y1;
102   gradient->radius0 = 0;
103   gradient->radius1 = 0;
104
105   gradient->ref_count = 1;
106
107   return gradient;
108 }
109
110 /**
111  * gtk_gradient_new_radial:
112  * @x0: X coordinate of the start circle
113  * @y0: Y coordinate of the start circle
114  * @radius0: radius of the start circle
115  * @x1: X coordinate of the end circle
116  * @y1: Y coordinate of the end circle
117  * @radius1: radius of the end circle
118  *
119  * Creates a new radial gradient along the two circles defined by (x0, y0, radius0) and
120  * (x1, y1, radius1). Before using the gradient a number of stop colors must be added
121  * through gtk_gradient_add_color_stop().
122  *
123  * Returns: A newly created #GtkGradient
124  *
125  * Since: 3.0
126  **/
127 GtkGradient *
128 gtk_gradient_new_radial (gdouble x0,
129                          gdouble y0,
130                          gdouble radius0,
131                          gdouble x1,
132                          gdouble y1,
133                          gdouble radius1)
134 {
135   GtkGradient *gradient;
136
137   gradient = g_slice_new (GtkGradient);
138   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
139
140   gradient->x0 = x0;
141   gradient->y0 = y0;
142   gradient->x1 = x1;
143   gradient->y1 = y1;
144   gradient->radius0 = radius0;
145   gradient->radius1 = radius1;
146
147   gradient->ref_count = 1;
148
149   return gradient;
150 }
151
152 /**
153  * gtk_gradient_add_color_stop:
154  * @gradient: a #GtkGradient
155  * @offset: offset for the color stop
156  * @color: color to use
157  *
158  * Adds a stop color to @gradient.
159  *
160  * Since: 3.0
161  **/
162 void
163 gtk_gradient_add_color_stop (GtkGradient      *gradient,
164                              gdouble           offset,
165                              GtkSymbolicColor *color)
166 {
167   ColorStop stop;
168
169   g_return_if_fail (gradient != NULL);
170
171   stop.offset = offset;
172   stop.color = gtk_symbolic_color_ref (color);
173
174   g_array_append_val (gradient->stops, stop);
175 }
176
177 /**
178  * gtk_gradient_ref:
179  * @gradient: a #GtkGradient
180  *
181  * Increases the reference count of @gradient.
182  *
183  * Returns: The same @gradient
184  *
185  * Since: 3.0
186  **/
187 GtkGradient *
188 gtk_gradient_ref (GtkGradient *gradient)
189 {
190   g_return_val_if_fail (gradient != NULL, NULL);
191
192   gradient->ref_count++;
193
194   return gradient;
195 }
196
197 /**
198  * gtk_gradient_unref:
199  * @gradient: a #GtkGradient
200  *
201  * Decreases the reference count of @gradient, freeing its memory
202  * if the reference count reaches 0.
203  *
204  * Since: 3.0
205  **/
206 void
207 gtk_gradient_unref (GtkGradient *gradient)
208 {
209   g_return_if_fail (gradient != NULL);
210
211   gradient->ref_count--;
212
213   if (gradient->ref_count == 0)
214     {
215       guint i;
216
217       for (i = 0; i < gradient->stops->len; i++)
218         {
219           ColorStop *stop;
220
221           stop = &g_array_index (gradient->stops, ColorStop, i);
222           gtk_symbolic_color_unref (stop->color);
223         }
224
225       g_array_free (gradient->stops, TRUE);
226       g_slice_free (GtkGradient, gradient);
227     }
228 }
229
230 /**
231  * gtk_gradient_resolve:
232  * @gradient: a #GtkGradient
233  * @props: #GtkStyleProperties to use when resolving named colors
234  * @resolved_gradient: (out): return location for the resolved pattern
235  *
236  * If @gradient is resolvable, @resolved_gradient will be filled in
237  * with the resolved gradient as a cairo_pattern_t, and %TRUE will
238  * be returned. Generally, if @gradient can't be resolved, it is
239  * due to it being defined on top of a named color that doesn't
240  * exist in @props.
241  *
242  * Returns: %TRUE if the gradient has been resolved
243  *
244  * Since: 3.0
245  **/
246 gboolean
247 gtk_gradient_resolve (GtkGradient         *gradient,
248                       GtkStyleProperties  *props,
249                       cairo_pattern_t    **resolved_gradient)
250 {
251   cairo_pattern_t *pattern;
252   guint i;
253
254   g_return_val_if_fail (gradient != NULL, FALSE);
255   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
256   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
257
258   if (gradient->radius0 == 0 && gradient->radius1 == 0)
259     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
260                                            gradient->x1, gradient->y1);
261   else
262     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
263                                            gradient->radius0,
264                                            gradient->x1, gradient->y1,
265                                            gradient->radius1);
266
267   for (i = 0; i < gradient->stops->len; i++)
268     {
269       ColorStop *stop;
270       GdkRGBA color;
271
272       stop = &g_array_index (gradient->stops, ColorStop, i);
273
274       if (!gtk_symbolic_color_resolve (stop->color, props, &color))
275         {
276           cairo_pattern_destroy (pattern);
277           return FALSE;
278         }
279
280       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
281                                          color.red, color.green,
282                                          color.blue, color.alpha);
283     }
284
285   *resolved_gradient = pattern;
286   return TRUE;
287 }
288
289 cairo_pattern_t *
290 _gtk_gradient_resolve_full (GtkGradient             *gradient,
291                             GtkStyleProviderPrivate *provider,
292                             GtkCssComputedValues    *values,
293                             GtkCssComputedValues    *parent_values,
294                             GtkCssDependencies      *dependencies)
295 {
296   cairo_pattern_t *pattern;
297   guint i;
298
299   g_return_val_if_fail (gradient != NULL, NULL);
300   g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
301   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
302   g_return_val_if_fail (parent_values == NULL || GTK_IS_CSS_COMPUTED_VALUES (parent_values), NULL);
303   g_return_val_if_fail (*dependencies == 0, NULL);
304
305   if (gradient->radius0 == 0 && gradient->radius1 == 0)
306     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
307                                            gradient->x1, gradient->y1);
308   else
309     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
310                                            gradient->radius0,
311                                            gradient->x1, gradient->y1,
312                                            gradient->radius1);
313
314   for (i = 0; i < gradient->stops->len; i++)
315     {
316       ColorStop *stop;
317       GtkCssValue *val;
318       GdkRGBA rgba;
319       GtkCssDependencies stop_deps;
320
321       stop = &g_array_index (gradient->stops, ColorStop, i);
322
323       /* if color resolving fails, assume transparency */
324       val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (stop->color),
325                                           provider,
326                                           _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_COLOR),
327                                           GTK_CSS_DEPENDS_ON_COLOR,
328                                           &stop_deps);
329       if (val)
330         {
331           rgba = *_gtk_css_rgba_value_get_rgba (val);
332           *dependencies = _gtk_css_dependencies_union (*dependencies, stop_deps);
333           _gtk_css_value_unref (val);
334         }
335       else
336         {
337           rgba.red = rgba.green = rgba.blue = rgba.alpha = 0.0;
338         }
339
340       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
341                                          rgba.red, rgba.green,
342                                          rgba.blue, rgba.alpha);
343     }
344
345   return pattern;
346 }
347
348 static void
349 append_number (GString    *str,
350                double      d,
351                const char *zero,
352                const char *half,
353                const char *one)
354 {
355   if (zero && d == 0.0)
356     g_string_append (str, zero);
357   else if (half && d == 0.5)
358     g_string_append (str, half);
359   else if (one && d == 1.0)
360     g_string_append (str, one);
361   else
362     {
363       char buf[G_ASCII_DTOSTR_BUF_SIZE];
364
365       g_ascii_dtostr (buf, sizeof (buf), d);
366       g_string_append (str, buf);
367     }
368 }
369
370 /**
371  * gtk_gradient_to_string:
372  * @gradient: the gradient to print
373  *
374  * Creates a string representation for @gradient that is suitable
375  * for using in GTK CSS files.
376  *
377  * Returns: A string representation for @gradient
378  **/
379 char *
380 gtk_gradient_to_string (GtkGradient *gradient)
381 {
382   GString *str;
383   guint i;
384
385   g_return_val_if_fail (gradient != NULL, NULL);
386
387   str = g_string_new ("-gtk-gradient (");
388
389   if (gradient->radius0 == 0 && gradient->radius1 == 0)
390     {
391       g_string_append (str, "linear, ");
392       append_number (str, gradient->x0, "left", "center", "right");
393       g_string_append_c (str, ' ');
394       append_number (str, gradient->y0, "top", "center", "bottom");
395       g_string_append (str, ", ");
396       append_number (str, gradient->x1, "left", "center", "right");
397       g_string_append_c (str, ' ');
398       append_number (str, gradient->y1, "top", "center", "bottom");
399     }
400   else
401     {
402       g_string_append (str, "radial, ");
403       append_number (str, gradient->x0, "left", "center", "right");
404       g_string_append_c (str, ' ');
405       append_number (str, gradient->y0, "top", "center", "bottom");
406       g_string_append (str, ", ");
407       append_number (str, gradient->radius0, NULL, NULL, NULL);
408       g_string_append (str, ", ");
409       append_number (str, gradient->x1, "left", "center", "right");
410       g_string_append_c (str, ' ');
411       append_number (str, gradient->y1, "top", "center", "bottom");
412       g_string_append (str, ", ");
413       append_number (str, gradient->radius1, NULL, NULL, NULL);
414     }
415   
416   for (i = 0; i < gradient->stops->len; i++)
417     {
418       ColorStop *stop;
419       char *s;
420
421       stop = &g_array_index (gradient->stops, ColorStop, i);
422
423       g_string_append (str, ", ");
424
425       if (stop->offset == 0.0)
426         g_string_append (str, "from (");
427       else if (stop->offset == 1.0)
428         g_string_append (str, "to (");
429       else
430         {
431           g_string_append (str, "color-stop (");
432           append_number (str, stop->offset, NULL, NULL, NULL);
433           g_string_append (str, ", ");
434         }
435
436       s = gtk_symbolic_color_to_string (stop->color);
437       g_string_append (str, s);
438       g_free (s);
439
440       g_string_append (str, ")");
441     }
442
443   g_string_append (str, ")");
444
445   return g_string_free (str, FALSE);
446 }
447
448 static GtkGradient *
449 gtk_gradient_fade (GtkGradient *gradient,
450                    double       opacity)
451 {
452   GtkGradient *faded;
453   guint i;
454
455   faded = g_slice_new (GtkGradient);
456   faded->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
457
458   faded->x0 = gradient->x0;
459   faded->y0 = gradient->y0;
460   faded->x1 = gradient->x1;
461   faded->y1 = gradient->y1;
462   faded->radius0 = gradient->radius0;
463   faded->radius1 = gradient->radius1;
464
465   faded->ref_count = 1;
466
467   for (i = 0; i < gradient->stops->len; i++)
468     {
469       GtkSymbolicColor *color;
470       ColorStop *stop;
471
472       stop = &g_array_index (gradient->stops, ColorStop, i);
473       color = gtk_symbolic_color_new_alpha (stop->color, opacity);
474       gtk_gradient_add_color_stop (faded, stop->offset, color);
475       gtk_symbolic_color_unref (color);
476     }
477
478   return faded;
479 }
480
481 GtkGradient *
482 _gtk_gradient_transition (GtkGradient *start,
483                           GtkGradient *end,
484                           guint        property_id,
485                           double       progress)
486 {
487   GtkGradient *gradient;
488   guint i;
489
490   g_return_val_if_fail (start != NULL, NULL);
491
492   if (end == NULL)
493     return gtk_gradient_fade (start, 1.0 - CLAMP (progress, 0.0, 1.0));
494
495   if (start->stops->len != end->stops->len)
496     return NULL;
497
498   /* check both are radial/linear */
499   if ((start->radius0 == 0 && start->radius1 == 0) != (end->radius0 == 0 && end->radius1 == 0))
500     return NULL;
501
502   gradient = g_slice_new (GtkGradient);
503   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
504
505   gradient->x0 = (1 - progress) * start->x0 + progress * end->x0;
506   gradient->y0 = (1 - progress) * start->y0 + progress * end->y0;
507   gradient->x1 = (1 - progress) * start->x1 + progress * end->x1;
508   gradient->y1 = (1 - progress) * start->y1 + progress * end->y1;
509   gradient->radius0 = (1 - progress) * start->radius0 + progress * end->radius0;
510   gradient->radius1 = (1 - progress) * start->radius1 + progress * end->radius1;
511
512   gradient->ref_count = 1;
513
514   for (i = 0; i < start->stops->len; i++)
515     {
516       ColorStop *start_stop, *end_stop;
517       GtkSymbolicColor *color;
518       double offset;
519
520       start_stop = &g_array_index (start->stops, ColorStop, i);
521       end_stop = &g_array_index (end->stops, ColorStop, i);
522
523       offset = (1 - progress) * start_stop->offset + progress * end_stop->offset;
524       color = gtk_symbolic_color_new_mix (start_stop->color,
525                                           end_stop->color,
526                                           progress);
527       gtk_gradient_add_color_stop (gradient, offset, color);
528       gtk_symbolic_color_unref (color);
529     }
530
531   return gradient;
532 }