]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssimagegradient.c
gradient: Add a private header file
[~andy/gtk] / gtk / gtkcssimagegradient.c
1 /*
2  * Copyright © 2011 Red Hat Inc.
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.1 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  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcssimagegradientprivate.h"
23
24 #include "gtkcssprovider.h"
25 #include "gtkgradientprivate.h"
26 #include "gtksymboliccolorprivate.h"
27
28 G_DEFINE_TYPE (GtkCssImageGradient, _gtk_css_image_gradient, GTK_TYPE_CSS_IMAGE)
29
30 static GtkCssImage *
31 gtk_css_image_gradient_compute (GtkCssImage             *image,
32                                 guint                    property_id,
33                                 GtkStyleProviderPrivate *provider,
34                                 GtkCssComputedValues    *values,
35                                 GtkCssComputedValues    *parent_values,
36                                 GtkCssDependencies      *dependencies)
37 {
38   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
39   GtkCssImageGradient *copy;
40
41   if (gradient->pattern)
42     return g_object_ref (gradient);
43
44   copy = g_object_new (GTK_TYPE_CSS_IMAGE_GRADIENT, NULL);
45   copy->gradient = gtk_gradient_ref (gradient->gradient);
46   copy->pattern = _gtk_gradient_resolve_full (copy->gradient, provider, values, parent_values, dependencies);
47
48   return GTK_CSS_IMAGE (copy);
49 }
50
51 static gboolean
52 gtk_css_image_gradient_draw_circle (GtkCssImageGradient *image,
53                                     cairo_t              *cr,
54                                     double               width,
55                                     double               height)
56 {
57   cairo_pattern_t *pattern = image->pattern;
58   double x0, y0, x1, y1, r0, r1;
59   GdkRGBA color0, color1;
60   double offset0, offset1;
61   int n_stops;
62
63   if (cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_RADIAL)
64     return FALSE;
65   if (cairo_pattern_get_extend (pattern) != CAIRO_EXTEND_PAD)
66     return FALSE;
67
68   cairo_pattern_get_radial_circles (pattern, &x0, &y0, &r0, &x1, &y1, &r1);
69
70   if (x0 != x1 ||
71       y0 != y1 ||
72       r0 != 0.0)
73     return FALSE;
74
75   cairo_pattern_get_color_stop_count (pattern, &n_stops);
76   if (n_stops != 2)
77     return FALSE;
78
79   cairo_pattern_get_color_stop_rgba (pattern, 0, &offset0, &color0.red, &color0.green, &color0.blue, &color0.alpha);
80   cairo_pattern_get_color_stop_rgba (pattern, 1, &offset1, &color1.red, &color1.green, &color1.blue, &color1.alpha);
81   if (offset0 != offset1)
82     return FALSE;
83
84   cairo_scale (cr, width, height);
85
86   cairo_rectangle (cr, 0, 0, 1, 1);
87   cairo_clip (cr);
88
89   gdk_cairo_set_source_rgba (cr, &color1);
90   cairo_paint (cr);
91
92   gdk_cairo_set_source_rgba (cr, &color0);
93   cairo_arc (cr, x1, y1, r1 * offset1, 0, 2 * G_PI);
94   cairo_fill (cr);
95
96   return TRUE;
97 }
98
99 static void
100 gtk_css_image_gradient_draw (GtkCssImage        *image,
101                              cairo_t            *cr,
102                              double              width,
103                              double              height)
104 {
105   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
106
107   if (!gradient->pattern)
108     {
109       g_warning ("trying to paint unresolved gradient");
110       return;
111     }
112
113   if (gtk_css_image_gradient_draw_circle (gradient, cr, width, height))
114     return;
115
116   cairo_scale (cr, width, height);
117
118   cairo_rectangle (cr, 0, 0, 1, 1);
119   cairo_set_source (cr, gradient->pattern);
120   cairo_fill (cr);
121 }
122
123 static gboolean
124 gtk_css_image_gradient_parse (GtkCssImage  *image,
125                               GtkCssParser *parser)
126 {
127   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
128
129   gradient->gradient = _gtk_gradient_parse (parser);
130
131   return gradient->gradient != NULL;
132 }
133
134 static void
135 gtk_css_image_gradient_print (GtkCssImage *image,
136                               GString     *string)
137 {
138   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
139   char *s;
140
141   s = gtk_gradient_to_string (gradient->gradient);
142   g_string_append (string, s);
143   g_free (s);
144 }
145
146 static void
147 gtk_css_image_gradient_dispose (GObject *object)
148 {
149   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (object);
150
151   if (gradient->gradient)
152     {
153       gtk_gradient_unref (gradient->gradient);
154       gradient->gradient = NULL;
155     }
156   if (gradient->pattern)
157     {
158       cairo_pattern_destroy (gradient->pattern);
159       gradient->pattern = NULL;
160     }
161
162   G_OBJECT_CLASS (_gtk_css_image_gradient_parent_class)->dispose (object);
163 }
164
165 static void
166 _gtk_css_image_gradient_class_init (GtkCssImageGradientClass *klass)
167 {
168   GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
169   GObjectClass *object_class = G_OBJECT_CLASS (klass);
170
171   image_class->compute = gtk_css_image_gradient_compute;
172   image_class->draw = gtk_css_image_gradient_draw;
173   image_class->parse = gtk_css_image_gradient_parse;
174   image_class->print = gtk_css_image_gradient_print;
175
176   object_class->dispose = gtk_css_image_gradient_dispose;
177 }
178
179 static void
180 _gtk_css_image_gradient_init (GtkCssImageGradient *image_gradient)
181 {
182 }
183
184 GtkGradient *
185 _gtk_gradient_parse (GtkCssParser *parser)
186 {
187   GtkGradient *gradient;
188   cairo_pattern_type_t type;
189   gdouble coords[6];
190   guint i;
191
192   g_return_val_if_fail (parser != NULL, NULL);
193
194   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
195     {
196       _gtk_css_parser_error (parser,
197                              "Expected '-gtk-gradient'");
198       return NULL;
199     }
200
201   if (!_gtk_css_parser_try (parser, "(", TRUE))
202     {
203       _gtk_css_parser_error (parser,
204                              "Expected '(' after '-gtk-gradient'");
205       return NULL;
206     }
207
208   /* Parse gradient type */
209   if (_gtk_css_parser_try (parser, "linear", TRUE))
210     type = CAIRO_PATTERN_TYPE_LINEAR;
211   else if (_gtk_css_parser_try (parser, "radial", TRUE))
212     type = CAIRO_PATTERN_TYPE_RADIAL;
213   else
214     {
215       _gtk_css_parser_error (parser,
216                              "Gradient type must be 'radial' or 'linear'");
217       return NULL;
218     }
219
220   /* Parse start/stop position parameters */
221   for (i = 0; i < 2; i++)
222     {
223       if (! _gtk_css_parser_try (parser, ",", TRUE))
224         {
225           _gtk_css_parser_error (parser,
226                                  "Expected ','");
227           return NULL;
228         }
229
230       if (_gtk_css_parser_try (parser, "left", TRUE))
231         coords[i * 3] = 0;
232       else if (_gtk_css_parser_try (parser, "right", TRUE))
233         coords[i * 3] = 1;
234       else if (_gtk_css_parser_try (parser, "center", TRUE))
235         coords[i * 3] = 0.5;
236       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
237         {
238           _gtk_css_parser_error (parser,
239                                  "Expected a valid X coordinate");
240           return NULL;
241         }
242
243       if (_gtk_css_parser_try (parser, "top", TRUE))
244         coords[i * 3 + 1] = 0;
245       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
246         coords[i * 3 + 1] = 1;
247       else if (_gtk_css_parser_try (parser, "center", TRUE))
248         coords[i * 3 + 1] = 0.5;
249       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
250         {
251           _gtk_css_parser_error (parser,
252                                  "Expected a valid Y coordinate");
253           return NULL;
254         }
255
256       if (type == CAIRO_PATTERN_TYPE_RADIAL)
257         {
258           /* Parse radius */
259           if (! _gtk_css_parser_try (parser, ",", TRUE))
260             {
261               _gtk_css_parser_error (parser,
262                                      "Expected ','");
263               return NULL;
264             }
265
266           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
267             {
268               _gtk_css_parser_error (parser,
269                                      "Expected a numer for the radius");
270               return NULL;
271             }
272         }
273     }
274
275   if (type == CAIRO_PATTERN_TYPE_LINEAR)
276     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
277   else
278     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
279                                         coords[3], coords[4], coords[5]);
280
281   while (_gtk_css_parser_try (parser, ",", TRUE))
282     {
283       GtkSymbolicColor *color;
284       gdouble position;
285
286       if (_gtk_css_parser_try (parser, "from", TRUE))
287         {
288           position = 0;
289
290           if (!_gtk_css_parser_try (parser, "(", TRUE))
291             {
292               gtk_gradient_unref (gradient);
293               _gtk_css_parser_error (parser,
294                                      "Expected '('");
295               return NULL;
296             }
297
298         }
299       else if (_gtk_css_parser_try (parser, "to", TRUE))
300         {
301           position = 1;
302
303           if (!_gtk_css_parser_try (parser, "(", TRUE))
304             {
305               gtk_gradient_unref (gradient);
306               _gtk_css_parser_error (parser,
307                                      "Expected '('");
308               return NULL;
309             }
310
311         }
312       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
313         {
314           if (!_gtk_css_parser_try (parser, "(", TRUE))
315             {
316               gtk_gradient_unref (gradient);
317               _gtk_css_parser_error (parser,
318                                      "Expected '('");
319               return NULL;
320             }
321
322           if (!_gtk_css_parser_try_double (parser, &position))
323             {
324               gtk_gradient_unref (gradient);
325               _gtk_css_parser_error (parser,
326                                      "Expected a valid number");
327               return NULL;
328             }
329
330           if (!_gtk_css_parser_try (parser, ",", TRUE))
331             {
332               gtk_gradient_unref (gradient);
333               _gtk_css_parser_error (parser,
334                                      "Expected a comma");
335               return NULL;
336             }
337         }
338       else
339         {
340           gtk_gradient_unref (gradient);
341           _gtk_css_parser_error (parser,
342                                  "Not a valid color-stop definition");
343           return NULL;
344         }
345
346       color = _gtk_symbolic_color_new_take_value (_gtk_css_symbolic_value_new (parser));
347       if (color == NULL)
348         {
349           gtk_gradient_unref (gradient);
350           return NULL;
351         }
352
353       gtk_gradient_add_color_stop (gradient, position, color);
354       gtk_symbolic_color_unref (color);
355
356       if (!_gtk_css_parser_try (parser, ")", TRUE))
357         {
358           gtk_gradient_unref (gradient);
359           _gtk_css_parser_error (parser,
360                                  "Expected ')'");
361           return NULL;
362         }
363     }
364
365   if (!_gtk_css_parser_try (parser, ")", TRUE))
366     {
367       gtk_gradient_unref (gradient);
368       _gtk_css_parser_error (parser,
369                              "Expected ')'");
370       return NULL;
371     }
372
373   return gradient;
374 }