1 /*
2  * Copyright © 2015 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: Matthias Clasen <mclasen@redhat.com>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkcssimageradialprivate.h"
23 
24 #include <math.h>
25 
26 #include "gtkcsscolorvalueprivate.h"
27 #include "gtkcssnumbervalueprivate.h"
28 #include "gtkcsspositionvalueprivate.h"
29 #include "gtkcssrgbavalueprivate.h"
30 #include "gtkcssprovider.h"
31 
G_DEFINE_TYPE(GtkCssImageRadial,_gtk_css_image_radial,GTK_TYPE_CSS_IMAGE)32 G_DEFINE_TYPE (GtkCssImageRadial, _gtk_css_image_radial, GTK_TYPE_CSS_IMAGE)
33 
34 static void
35 gtk_css_image_radial_get_start_end (GtkCssImageRadial *radial,
36                                     double             radius,
37                                     double            *start,
38                                     double            *end)
39 {
40   GtkCssImageRadialColorStop *stop;
41   double pos;
42   guint i;
43 
44   if (radial->repeating)
45     {
46       stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, 0);
47       if (stop->offset == NULL)
48         *start = 0;
49       else
50         *start = _gtk_css_number_value_get (stop->offset, radius) / radius;
51 
52       *end = *start;
53 
54       for (i = 0; i < radial->stops->len; i++)
55         {
56           stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, i);
57 
58           if (stop->offset == NULL)
59             continue;
60 
61           pos = _gtk_css_number_value_get (stop->offset, radius) / radius;
62 
63           *end = MAX (pos, *end);
64         }
65 
66       if (stop->offset == NULL)
67         *end = MAX (*end, 1.0);
68     }
69   else
70     {
71       *start = 0;
72       *end = 1;
73     }
74 }
75 
76 static void
gtk_css_image_radial_draw(GtkCssImage * image,cairo_t * cr,double width,double height)77 gtk_css_image_radial_draw (GtkCssImage *image,
78                            cairo_t     *cr,
79                            double       width,
80                            double       height)
81 {
82   GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image);
83   cairo_pattern_t *pattern;
84   cairo_matrix_t matrix;
85   double x, y;
86   double radius, yscale;
87   double start, end;
88   double r1, r2, r3, r4, r;
89   double offset;
90   int i, last;
91 
92   x = _gtk_css_position_value_get_x (radial->position, width);
93   y = _gtk_css_position_value_get_y (radial->position, height);
94 
95   if (radial->circle)
96     {
97       switch (radial->size)
98         {
99         case GTK_CSS_EXPLICIT_SIZE:
100           radius = _gtk_css_number_value_get (radial->sizes[0], width);
101           break;
102         case GTK_CSS_CLOSEST_SIDE:
103           radius = MIN (MIN (x, width - x), MIN (y, height - y));
104           break;
105         case GTK_CSS_FARTHEST_SIDE:
106           radius = MAX (MAX (x, width - x), MAX (y, height - y));
107           break;
108         case GTK_CSS_CLOSEST_CORNER:
109         case GTK_CSS_FARTHEST_CORNER:
110           r1 = x*x + y*y;
111           r2 = x*x + (height - y)*(height - y);
112           r3 = (width - x)*(width - x) + y*y;
113           r4 = (width - x)*(width - x) + (height - y)*(height - y);
114           if (radial->size == GTK_CSS_CLOSEST_CORNER)
115             r = MIN ( MIN (r1, r2), MIN (r3, r4));
116           else
117             r = MAX ( MAX (r1, r2), MAX (r3, r4));
118           radius = sqrt (r);
119           break;
120         default:
121           g_assert_not_reached ();
122         }
123 
124       radius = MAX (1.0, radius);
125       yscale = 1.0;
126     }
127   else
128     {
129       double hradius, vradius;
130 
131       switch (radial->size)
132         {
133         case GTK_CSS_EXPLICIT_SIZE:
134           hradius = _gtk_css_number_value_get (radial->sizes[0], width);
135           vradius = _gtk_css_number_value_get (radial->sizes[1], height);
136           break;
137         case GTK_CSS_CLOSEST_SIDE:
138           hradius = MIN (x, width - x);
139           vradius = MIN (y, height - y);
140           break;
141         case GTK_CSS_FARTHEST_SIDE:
142           hradius = MAX (x, width - x);
143           vradius = MAX (y, height - y);
144           break;
145         case GTK_CSS_CLOSEST_CORNER:
146           hradius = M_SQRT2 * MIN (x, width - x);
147           vradius = M_SQRT2 * MIN (y, height - y);
148           break;
149         case GTK_CSS_FARTHEST_CORNER:
150           hradius = M_SQRT2 * MAX (x, width - x);
151           vradius = M_SQRT2 * MAX (y, height - y);
152           break;
153         default:
154           g_assert_not_reached ();
155         }
156 
157       hradius = MAX (1.0, hradius);
158       vradius = MAX (1.0, vradius);
159 
160       radius = hradius;
161       yscale = vradius / hradius;
162     }
163 
164   gtk_css_image_radial_get_start_end (radial, radius, &start, &end);
165 
166   pattern = cairo_pattern_create_radial (0, 0, 0, 0, 0, radius);
167   if (yscale != 1.0)
168     {
169       cairo_matrix_init_scale (&matrix, 1.0, 1.0 / yscale);
170       cairo_pattern_set_matrix (pattern, &matrix);
171     }
172 
173  if (radial->repeating)
174     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
175   else
176     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
177 
178   offset = start;
179   last = -1;
180   for (i = 0; i < radial->stops->len; i++)
181     {
182       GtkCssImageRadialColorStop *stop;
183       double pos, step;
184 
185       stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, i);
186 
187       if (stop->offset == NULL)
188         {
189           if (i == 0)
190             pos = 0.0;
191           else if (i + 1 == radial->stops->len)
192             pos = 1.0;
193           else
194             continue;
195         }
196       else
197         pos = _gtk_css_number_value_get (stop->offset, radius) / radius;
198 
199       pos = MAX (pos, 0);
200       step = pos / (i - last);
201       for (last = last + 1; last <= i; last++)
202         {
203           const GdkRGBA *rgba;
204 
205           stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, last);
206 
207           rgba = _gtk_css_rgba_value_get_rgba (stop->color);
208           offset += step;
209 
210           cairo_pattern_add_color_stop_rgba (pattern,
211                                              (offset - start) / (end - start),
212                                              rgba->red,
213                                              rgba->green,
214                                              rgba->blue,
215                                              rgba->alpha);
216         }
217 
218       offset = pos;
219       last = i;
220     }
221 
222   cairo_rectangle (cr, 0, 0, width, height);
223   cairo_translate (cr, x, y);
224   cairo_set_source (cr, pattern);
225   cairo_fill (cr);
226 
227   cairo_pattern_destroy (pattern);
228 }
229 
230 static gboolean
gtk_css_image_radial_parse(GtkCssImage * image,GtkCssParser * parser)231 gtk_css_image_radial_parse (GtkCssImage  *image,
232                             GtkCssParser *parser)
233 {
234   GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image);
235   gboolean has_shape = FALSE;
236   gboolean has_size = FALSE;
237   gboolean found_one = FALSE;
238   guint i;
239   static struct {
240     const char *name;
241     guint       value;
242   } names[] = {
243     { "closest-side", GTK_CSS_CLOSEST_SIDE },
244     { "farthest-side", GTK_CSS_FARTHEST_SIDE },
245     { "closest-corner", GTK_CSS_CLOSEST_CORNER },
246     { "farthest-corner", GTK_CSS_FARTHEST_CORNER }
247   };
248 
249   if (_gtk_css_parser_try (parser, "repeating-radial-gradient(", TRUE))
250     radial->repeating = TRUE;
251   else if (_gtk_css_parser_try (parser, "radial-gradient(", TRUE))
252     radial->repeating = FALSE;
253   else
254     {
255       _gtk_css_parser_error (parser, "Not a radial gradient");
256       return FALSE;
257     }
258 
259   do {
260     found_one = FALSE;
261     if (!has_shape && _gtk_css_parser_try (parser, "circle", TRUE))
262       {
263         radial->circle = TRUE;
264         found_one = has_shape = TRUE;
265       }
266     else if (!has_shape && _gtk_css_parser_try (parser, "ellipse", TRUE))
267       {
268         radial->circle = FALSE;
269         found_one = has_shape = TRUE;
270       }
271     else if (!has_size)
272       {
273         for (i = 0; i < G_N_ELEMENTS (names); i++)
274           {
275             if (_gtk_css_parser_try (parser, names[i].name, TRUE))
276               {
277                 found_one = has_size = TRUE;
278                 radial->size = names[i].value;
279                 break;
280               }
281           }
282 
283         if (!has_size)
284           {
285             if (gtk_css_number_value_can_parse (parser))
286               radial->sizes[0] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT);
287             if (gtk_css_number_value_can_parse (parser))
288               radial->sizes[1] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT);
289             found_one = has_size = radial->sizes[0] != NULL;
290           }
291       }
292 
293   } while (found_one && !(has_shape && has_size));
294 
295   if (_gtk_css_parser_try (parser, "at", TRUE))
296     {
297       radial->position = _gtk_css_position_value_parse (parser);
298       if (!radial->position)
299         return FALSE;
300       if (!_gtk_css_parser_try (parser, ",", TRUE))
301         {
302           _gtk_css_parser_error (parser, "Expected a comma here");
303           return FALSE;
304         }
305     }
306   else
307     {
308       radial->position = _gtk_css_position_value_new (_gtk_css_number_value_new (50, GTK_CSS_PERCENT),
309                                                       _gtk_css_number_value_new (50, GTK_CSS_PERCENT));
310 
311       if ((has_shape || has_size) &&
312           !_gtk_css_parser_try (parser, ",", TRUE))
313         {
314           _gtk_css_parser_error (parser, "Expected a comma here");
315           return FALSE;
316         }
317     }
318 
319   if (!has_size)
320     {
321       radial->size = GTK_CSS_FARTHEST_CORNER;
322     }
323 
324   if (!has_shape)
325     {
326       if (radial->sizes[0] && radial->sizes[1])
327         radial->circle = FALSE;
328       else
329         radial->circle = TRUE;
330     }
331 
332   if (has_shape && radial->circle)
333     {
334       if (radial->sizes[0] && radial->sizes[1])
335         {
336           _gtk_css_parser_error (parser, "Circular gradient can only have one size");
337           return FALSE;
338         }
339 
340       if (radial->sizes[0] && gtk_css_number_value_has_percent (radial->sizes[0]))
341         {
342           _gtk_css_parser_error (parser, "Circular gradient cannot have percentage as size");
343           return FALSE;
344         }
345     }
346 
347   if (has_size && !radial->circle)
348     {
349       if (radial->sizes[0] && !radial->sizes[1])
350         radial->sizes[1] = _gtk_css_value_ref (radial->sizes[0]);
351     }
352 
353   do {
354     GtkCssImageRadialColorStop stop;
355 
356     stop.color = _gtk_css_color_value_parse (parser);
357     if (stop.color == NULL)
358       return FALSE;
359 
360     if (gtk_css_number_value_can_parse (parser))
361       {
362         stop.offset = _gtk_css_number_value_parse (parser,
363                                                    GTK_CSS_PARSE_PERCENT
364                                                    | GTK_CSS_PARSE_LENGTH);
365         if (stop.offset == NULL)
366           {
367             _gtk_css_value_unref (stop.color);
368             return FALSE;
369           }
370       }
371     else
372       {
373         stop.offset = NULL;
374       }
375 
376     g_array_append_val (radial->stops, stop);
377 
378   } while (_gtk_css_parser_try (parser, ",", TRUE));
379 
380   if (radial->stops->len < 2)
381     {
382       _gtk_css_parser_error_full (parser,
383                                   GTK_CSS_PROVIDER_ERROR_DEPRECATED,
384                                   "Using one color stop with %s() is deprecated.",
385                                   radial->repeating ? "repeating-radial-gradient" : "radial-gradient");
386     }
387 
388   if (!_gtk_css_parser_try (parser, ")", TRUE))
389     {
390       _gtk_css_parser_error (parser, "Missing closing bracket at end of radial gradient");
391       return FALSE;
392     }
393 
394   return TRUE;
395 }
396 
397 static void
gtk_css_image_radial_print(GtkCssImage * image,GString * string)398 gtk_css_image_radial_print (GtkCssImage *image,
399                             GString     *string)
400 {
401   GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image);
402   guint i;
403   const gchar *names[] = {
404     NULL,
405     "closest-side",
406     "farthest-side",
407     "closest-corner",
408     "farthest-corner"
409   };
410 
411   if (radial->repeating)
412     g_string_append (string, "repeating-radial-gradient(");
413   else
414     g_string_append (string, "radial-gradient(");
415 
416   if (radial->circle)
417     g_string_append (string, "circle ");
418   else
419     g_string_append (string, "ellipse ");
420 
421   if (radial->size != 0)
422     g_string_append (string, names[radial->size]);
423   else
424     {
425       if (radial->sizes[0])
426         _gtk_css_value_print (radial->sizes[0], string);
427       if (radial->sizes[1])
428         {
429           g_string_append (string, " ");
430           _gtk_css_value_print (radial->sizes[1], string);
431         }
432     }
433 
434   g_string_append (string, " at ");
435   _gtk_css_value_print (radial->position, string);
436 
437   g_string_append (string, ", ");
438 
439   for (i = 0; i < radial->stops->len; i++)
440     {
441       GtkCssImageRadialColorStop *stop;
442 
443       if (i > 0)
444         g_string_append (string, ", ");
445 
446       stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, i);
447 
448       _gtk_css_value_print (stop->color, string);
449 
450       if (stop->offset)
451         {
452           g_string_append (string, " ");
453           _gtk_css_value_print (stop->offset, string);
454         }
455     }
456 
457   g_string_append (string, ")");
458 }
459 
460 static GtkCssImage *
gtk_css_image_radial_compute(GtkCssImage * image,guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)461 gtk_css_image_radial_compute (GtkCssImage             *image,
462                               guint                    property_id,
463                               GtkStyleProviderPrivate *provider,
464                               GtkCssStyle             *style,
465                               GtkCssStyle             *parent_style)
466 {
467   GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image);
468   GtkCssImageRadial *copy;
469   guint i;
470 
471   copy = g_object_new (GTK_TYPE_CSS_IMAGE_RADIAL, NULL);
472   copy->repeating = radial->repeating;
473   copy->circle = radial->circle;
474   copy->size = radial->size;
475 
476   copy->position = _gtk_css_value_compute (radial->position, property_id, provider, style, parent_style);
477 
478   if (radial->sizes[0])
479     copy->sizes[0] = _gtk_css_value_compute (radial->sizes[0], property_id, provider, style, parent_style);
480 
481   if (radial->sizes[1])
482     copy->sizes[1] = _gtk_css_value_compute (radial->sizes[1], property_id, provider, style, parent_style);
483 
484   g_array_set_size (copy->stops, radial->stops->len);
485   for (i = 0; i < radial->stops->len; i++)
486     {
487       GtkCssImageRadialColorStop *stop, *scopy;
488 
489       stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, i);
490       scopy = &g_array_index (copy->stops, GtkCssImageRadialColorStop, i);
491 
492       scopy->color = _gtk_css_value_compute (stop->color, property_id, provider, style, parent_style);
493 
494       if (stop->offset)
495         {
496           scopy->offset = _gtk_css_value_compute (stop->offset, property_id, provider, style, parent_style);
497         }
498       else
499         {
500           scopy->offset = NULL;
501         }
502     }
503 
504   return GTK_CSS_IMAGE (copy);
505 }
506 
507 static GtkCssImage *
gtk_css_image_radial_transition(GtkCssImage * start_image,GtkCssImage * end_image,guint property_id,double progress)508 gtk_css_image_radial_transition (GtkCssImage *start_image,
509                                  GtkCssImage *end_image,
510                                  guint        property_id,
511                                  double       progress)
512 {
513   GtkCssImageRadial *start, *end, *result;
514   guint i;
515 
516   start = GTK_CSS_IMAGE_RADIAL (start_image);
517 
518   if (end_image == NULL)
519     return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress);
520 
521   if (!GTK_IS_CSS_IMAGE_RADIAL (end_image))
522     return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress);
523 
524   end = GTK_CSS_IMAGE_RADIAL (end_image);
525 
526   if (start->repeating != end->repeating ||
527       start->stops->len != end->stops->len ||
528       start->size != end->size ||
529       start->circle != end->circle)
530     return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress);
531 
532   result = g_object_new (GTK_TYPE_CSS_IMAGE_RADIAL, NULL);
533   result->repeating = start->repeating;
534   result->circle = start->circle;
535   result->size = start->size;
536 
537   result->position = _gtk_css_value_transition (start->position, end->position, property_id, progress);
538   if (result->position == NULL)
539     goto fail;
540 
541   if (start->sizes[0] && end->sizes[0])
542     {
543       result->sizes[0] = _gtk_css_value_transition (start->sizes[0], end->sizes[0], property_id, progress);
544       if (result->sizes[0] == NULL)
545         goto fail;
546     }
547   else
548     result->sizes[0] = 0;
549 
550   if (start->sizes[1] && end->sizes[1])
551     {
552       result->sizes[1] = _gtk_css_value_transition (start->sizes[1], end->sizes[1], property_id, progress);
553       if (result->sizes[1] == NULL)
554         goto fail;
555     }
556   else
557     result->sizes[1] = 0;
558 
559   for (i = 0; i < start->stops->len; i++)
560     {
561       GtkCssImageRadialColorStop stop, *start_stop, *end_stop;
562 
563       start_stop = &g_array_index (start->stops, GtkCssImageRadialColorStop, i);
564       end_stop = &g_array_index (end->stops, GtkCssImageRadialColorStop, i);
565 
566       if ((start_stop->offset != NULL) != (end_stop->offset != NULL))
567         goto fail;
568 
569       if (start_stop->offset == NULL)
570         {
571           stop.offset = NULL;
572         }
573       else
574         {
575           stop.offset = _gtk_css_value_transition (start_stop->offset,
576                                                    end_stop->offset,
577                                                    property_id,
578                                                    progress);
579           if (stop.offset == NULL)
580             goto fail;
581         }
582 
583       stop.color = _gtk_css_value_transition (start_stop->color,
584                                               end_stop->color,
585                                               property_id,
586                                               progress);
587       if (stop.color == NULL)
588         {
589           if (stop.offset)
590             _gtk_css_value_unref (stop.offset);
591           goto fail;
592         }
593 
594       g_array_append_val (result->stops, stop);
595     }
596 
597   return GTK_CSS_IMAGE (result);
598 
599 fail:
600   g_object_unref (result);
601   return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress);
602 }
603 
604 static gboolean
gtk_css_image_radial_equal(GtkCssImage * image1,GtkCssImage * image2)605 gtk_css_image_radial_equal (GtkCssImage *image1,
606                             GtkCssImage *image2)
607 {
608   GtkCssImageRadial *radial1 = GTK_CSS_IMAGE_RADIAL (image1);
609   GtkCssImageRadial *radial2 = GTK_CSS_IMAGE_RADIAL (image2);
610   guint i;
611 
612   if (radial1->repeating != radial2->repeating ||
613       radial1->size != radial2->size ||
614       !_gtk_css_value_equal (radial1->position, radial2->position) ||
615       ((radial1->sizes[0] == NULL) != (radial2->sizes[0] == NULL)) ||
616       (radial1->sizes[0] && radial2->sizes[0] && !_gtk_css_value_equal (radial1->sizes[0], radial2->sizes[0])) ||
617       ((radial1->sizes[1] == NULL) != (radial2->sizes[1] == NULL)) ||
618       (radial1->sizes[1] && radial2->sizes[1] && !_gtk_css_value_equal (radial1->sizes[1], radial2->sizes[1])) ||
619       radial1->stops->len != radial2->stops->len)
620     return FALSE;
621 
622   for (i = 0; i < radial1->stops->len; i++)
623     {
624       GtkCssImageRadialColorStop *stop1, *stop2;
625 
626       stop1 = &g_array_index (radial1->stops, GtkCssImageRadialColorStop, i);
627       stop2 = &g_array_index (radial2->stops, GtkCssImageRadialColorStop, i);
628 
629       if (!_gtk_css_value_equal0 (stop1->offset, stop2->offset) ||
630           !_gtk_css_value_equal (stop1->color, stop2->color))
631         return FALSE;
632     }
633 
634   return TRUE;
635 }
636 
637 static void
gtk_css_image_radial_dispose(GObject * object)638 gtk_css_image_radial_dispose (GObject *object)
639 {
640   GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (object);
641   int i;
642 
643   if (radial->stops)
644     {
645       g_array_free (radial->stops, TRUE);
646       radial->stops = NULL;
647     }
648 
649   if (radial->position)
650     {
651       _gtk_css_value_unref (radial->position);
652       radial->position = NULL;
653     }
654 
655   for (i = 0; i < 2; i++)
656     if (radial->sizes[i])
657       {
658         _gtk_css_value_unref (radial->sizes[i]);
659         radial->sizes[i] = NULL;
660       }
661 
662   G_OBJECT_CLASS (_gtk_css_image_radial_parent_class)->dispose (object);
663 }
664 
665 static void
_gtk_css_image_radial_class_init(GtkCssImageRadialClass * klass)666 _gtk_css_image_radial_class_init (GtkCssImageRadialClass *klass)
667 {
668   GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
669   GObjectClass *object_class = G_OBJECT_CLASS (klass);
670 
671   image_class->draw = gtk_css_image_radial_draw;
672   image_class->parse = gtk_css_image_radial_parse;
673   image_class->print = gtk_css_image_radial_print;
674   image_class->compute = gtk_css_image_radial_compute;
675   image_class->transition = gtk_css_image_radial_transition;
676   image_class->equal = gtk_css_image_radial_equal;
677 
678   object_class->dispose = gtk_css_image_radial_dispose;
679 }
680 
681 static void
gtk_css_image_clear_color_stop(gpointer color_stop)682 gtk_css_image_clear_color_stop (gpointer color_stop)
683 {
684   GtkCssImageRadialColorStop *stop = color_stop;
685 
686   _gtk_css_value_unref (stop->color);
687   if (stop->offset)
688     _gtk_css_value_unref (stop->offset);
689 }
690 
691 static void
_gtk_css_image_radial_init(GtkCssImageRadial * radial)692 _gtk_css_image_radial_init (GtkCssImageRadial *radial)
693 {
694   radial->stops = g_array_new (FALSE, FALSE, sizeof (GtkCssImageRadialColorStop));
695   g_array_set_clear_func (radial->stops, gtk_css_image_clear_color_stop);
696 }
697 
698