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