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 "gtkcsscolorvalueprivate.h" |
30 | #include "gtkcssprovider.h" |
31 | |
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 | const GtkCssImageRadialColorStop *stop; |
41 | double pos; |
42 | guint i; |
43 | |
44 | if (radial->repeating) |
45 | { |
46 | stop = &radial->color_stops[0]; |
47 | if (stop->offset == NULL) |
48 | *start = 0; |
49 | else |
50 | *start = _gtk_css_number_value_get (number: stop->offset, one_hundred_percent: radius) / radius; |
51 | |
52 | *end = *start; |
53 | |
54 | for (i = 0; i < radial->n_stops; i++) |
55 | { |
56 | stop = &radial->color_stops[i]; |
57 | |
58 | if (stop->offset == NULL) |
59 | continue; |
60 | |
61 | pos = _gtk_css_number_value_get (number: stop->offset, one_hundred_percent: 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 |
77 | gtk_css_image_radial_snapshot (GtkCssImage *image, |
78 | GtkSnapshot *snapshot, |
79 | double width, |
80 | double height) |
81 | { |
82 | GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); |
83 | GskColorStop *stops; |
84 | double x, y; |
85 | double hradius, vradius; |
86 | double start, end; |
87 | double r1, r2, r3, r4, r; |
88 | double offset; |
89 | int i, last; |
90 | |
91 | x = _gtk_css_position_value_get_x (position: radial->position, one_hundred_percent: width); |
92 | y = _gtk_css_position_value_get_y (position: radial->position, one_hundred_percent: height); |
93 | |
94 | if (radial->circle) |
95 | { |
96 | switch (radial->size) |
97 | { |
98 | case GTK_CSS_EXPLICIT_SIZE: |
99 | hradius = _gtk_css_number_value_get (number: radial->sizes[0], one_hundred_percent: width); |
100 | break; |
101 | case GTK_CSS_CLOSEST_SIDE: |
102 | hradius = MIN (MIN (x, width - x), MIN (y, height - y)); |
103 | break; |
104 | case GTK_CSS_FARTHEST_SIDE: |
105 | hradius = MAX (MAX (x, width - x), MAX (y, height - y)); |
106 | break; |
107 | case GTK_CSS_CLOSEST_CORNER: |
108 | case GTK_CSS_FARTHEST_CORNER: |
109 | r1 = x*x + y*y; |
110 | r2 = x*x + (height - y)*(height - y); |
111 | r3 = (width - x)*(width - x) + y*y; |
112 | r4 = (width - x)*(width - x) + (height - y)*(height - y); |
113 | if (radial->size == GTK_CSS_CLOSEST_CORNER) |
114 | r = MIN ( MIN (r1, r2), MIN (r3, r4)); |
115 | else |
116 | r = MAX ( MAX (r1, r2), MAX (r3, r4)); |
117 | hradius = sqrt (x: r); |
118 | break; |
119 | default: |
120 | g_assert_not_reached (); |
121 | } |
122 | |
123 | hradius = MAX (1.0, hradius); |
124 | vradius = hradius; |
125 | } |
126 | else |
127 | { |
128 | switch (radial->size) |
129 | { |
130 | case GTK_CSS_EXPLICIT_SIZE: |
131 | hradius = _gtk_css_number_value_get (number: radial->sizes[0], one_hundred_percent: width); |
132 | vradius = _gtk_css_number_value_get (number: radial->sizes[1], one_hundred_percent: height); |
133 | break; |
134 | case GTK_CSS_CLOSEST_SIDE: |
135 | hradius = MIN (x, width - x); |
136 | vradius = MIN (y, height - y); |
137 | break; |
138 | case GTK_CSS_FARTHEST_SIDE: |
139 | hradius = MAX (x, width - x); |
140 | vradius = MAX (y, height - y); |
141 | break; |
142 | case GTK_CSS_CLOSEST_CORNER: |
143 | hradius = M_SQRT2 * MIN (x, width - x); |
144 | vradius = M_SQRT2 * MIN (y, height - y); |
145 | break; |
146 | case GTK_CSS_FARTHEST_CORNER: |
147 | hradius = M_SQRT2 * MAX (x, width - x); |
148 | vradius = M_SQRT2 * MAX (y, height - y); |
149 | break; |
150 | default: |
151 | g_assert_not_reached (); |
152 | } |
153 | |
154 | hradius = MAX (1.0, hradius); |
155 | vradius = MAX (1.0, vradius); |
156 | } |
157 | |
158 | gtk_css_image_radial_get_start_end (radial, radius: hradius, start: &start, end: &end); |
159 | |
160 | offset = start; |
161 | last = -1; |
162 | stops = g_newa (GskColorStop, radial->n_stops); |
163 | |
164 | for (i = 0; i < radial->n_stops; i++) |
165 | { |
166 | const GtkCssImageRadialColorStop *stop = &radial->color_stops[i]; |
167 | double pos, step; |
168 | |
169 | if (stop->offset == NULL) |
170 | { |
171 | if (i == 0) |
172 | pos = 0.0; |
173 | else if (i + 1 == radial->n_stops) |
174 | pos = 1.0; |
175 | else |
176 | continue; |
177 | } |
178 | else |
179 | pos = MIN (1.0, _gtk_css_number_value_get (stop->offset, hradius) / hradius); |
180 | |
181 | pos = MAX (pos, offset); |
182 | step = (pos - offset) / (i - last); |
183 | for (last = last + 1; last <= i; last++) |
184 | { |
185 | stop = &radial->color_stops[last]; |
186 | |
187 | offset += step; |
188 | |
189 | stops[last].offset = (offset - start) / (end - start); |
190 | stops[last].color = *gtk_css_color_value_get_rgba (color: stop->color); |
191 | } |
192 | |
193 | offset = pos; |
194 | last = i; |
195 | } |
196 | |
197 | if (radial->repeating) |
198 | gtk_snapshot_append_repeating_radial_gradient (snapshot, |
199 | bounds: &GRAPHENE_RECT_INIT (0, 0, width, height), |
200 | center: &GRAPHENE_POINT_INIT (x, y), |
201 | hradius, |
202 | vradius, |
203 | start, |
204 | end, |
205 | stops, |
206 | n_stops: radial->n_stops); |
207 | else |
208 | gtk_snapshot_append_radial_gradient (snapshot, |
209 | bounds: &GRAPHENE_RECT_INIT (0, 0, width, height), |
210 | center: &GRAPHENE_POINT_INIT (x, y), |
211 | hradius, |
212 | vradius, |
213 | start, |
214 | end, |
215 | stops, |
216 | n_stops: radial->n_stops); |
217 | } |
218 | |
219 | static guint |
220 | gtk_css_image_radial_parse_color_stop (GtkCssImageRadial *radial, |
221 | GtkCssParser *parser, |
222 | GArray *stop_array) |
223 | { |
224 | GtkCssImageRadialColorStop stop; |
225 | |
226 | stop.color = _gtk_css_color_value_parse (parser); |
227 | if (stop.color == NULL) |
228 | return 0; |
229 | |
230 | if (gtk_css_number_value_can_parse (parser)) |
231 | { |
232 | stop.offset = _gtk_css_number_value_parse (parser, |
233 | flags: GTK_CSS_PARSE_PERCENT |
234 | | GTK_CSS_PARSE_LENGTH); |
235 | if (stop.offset == NULL) |
236 | { |
237 | _gtk_css_value_unref (value: stop.color); |
238 | return 0; |
239 | } |
240 | } |
241 | else |
242 | { |
243 | stop.offset = NULL; |
244 | } |
245 | |
246 | g_array_append_val (stop_array, stop); |
247 | |
248 | return 1; |
249 | } |
250 | |
251 | static guint |
252 | gtk_css_image_radial_parse_first_arg (GtkCssImageRadial *radial, |
253 | GtkCssParser *parser, |
254 | GArray *stop_array) |
255 | { |
256 | gboolean has_shape = FALSE; |
257 | gboolean has_size = FALSE; |
258 | gboolean found_one = FALSE; |
259 | guint i; |
260 | static struct { |
261 | const char *name; |
262 | guint value; |
263 | } names[] = { |
264 | { "closest-side" , GTK_CSS_CLOSEST_SIDE }, |
265 | { "farthest-side" , GTK_CSS_FARTHEST_SIDE }, |
266 | { "closest-corner" , GTK_CSS_CLOSEST_CORNER }, |
267 | { "farthest-corner" , GTK_CSS_FARTHEST_CORNER } |
268 | }; |
269 | |
270 | found_one = FALSE; |
271 | |
272 | do { |
273 | if (!has_shape && gtk_css_parser_try_ident (self: parser, ident: "circle" )) |
274 | { |
275 | radial->circle = TRUE; |
276 | found_one = has_shape = TRUE; |
277 | } |
278 | else if (!has_shape && gtk_css_parser_try_ident (self: parser, ident: "ellipse" )) |
279 | { |
280 | radial->circle = FALSE; |
281 | found_one = has_shape = TRUE; |
282 | } |
283 | else if (!has_size) |
284 | { |
285 | for (i = 0; i < G_N_ELEMENTS (names); i++) |
286 | { |
287 | if (gtk_css_parser_try_ident (self: parser, ident: names[i].name)) |
288 | { |
289 | found_one = has_size = TRUE; |
290 | radial->size = names[i].value; |
291 | break; |
292 | } |
293 | } |
294 | |
295 | if (!has_size && gtk_css_number_value_can_parse (parser)) |
296 | { |
297 | radial->sizes[0] = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT); |
298 | if (radial->sizes[0] == NULL) |
299 | return 0; |
300 | if (gtk_css_number_value_can_parse (parser)) |
301 | { |
302 | radial->sizes[1] = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT); |
303 | if (radial->sizes[1] == NULL) |
304 | return 0; |
305 | } |
306 | found_one = has_size = TRUE; |
307 | } |
308 | if (!has_size) |
309 | break; |
310 | } |
311 | else |
312 | { |
313 | break; |
314 | } |
315 | } while (!(has_shape && has_size)); |
316 | |
317 | if (gtk_css_parser_try_ident (self: parser, ident: "at" )) |
318 | { |
319 | radial->position = _gtk_css_position_value_parse (parser); |
320 | if (!radial->position) |
321 | return 0; |
322 | found_one = TRUE; |
323 | } |
324 | else |
325 | { |
326 | radial->position = _gtk_css_position_value_new (x: _gtk_css_number_value_new (value: 50, unit: GTK_CSS_PERCENT), |
327 | y: _gtk_css_number_value_new (value: 50, unit: GTK_CSS_PERCENT)); |
328 | } |
329 | |
330 | if (!has_size) |
331 | { |
332 | radial->size = GTK_CSS_FARTHEST_CORNER; |
333 | } |
334 | |
335 | if (!has_shape) |
336 | { |
337 | if (radial->sizes[0] && !radial->sizes[1]) |
338 | radial->circle = TRUE; |
339 | else |
340 | radial->circle = FALSE; |
341 | } |
342 | |
343 | if (has_shape && radial->circle) |
344 | { |
345 | if (radial->sizes[0] && radial->sizes[1]) |
346 | { |
347 | gtk_css_parser_error_syntax (self: parser, format: "Circular gradient can only have one size" ); |
348 | return 0; |
349 | } |
350 | |
351 | if (radial->sizes[0] && gtk_css_number_value_has_percent (value: radial->sizes[0])) |
352 | { |
353 | gtk_css_parser_error_syntax (self: parser, format: "Circular gradient cannot have percentage as size" ); |
354 | return 0; |
355 | } |
356 | } |
357 | |
358 | if (has_size && !radial->circle) |
359 | { |
360 | if (radial->sizes[0] && !radial->sizes[1]) |
361 | radial->sizes[1] = _gtk_css_value_ref (value: radial->sizes[0]); |
362 | } |
363 | |
364 | if (found_one) |
365 | return 1; |
366 | |
367 | if (!gtk_css_image_radial_parse_color_stop (radial, parser, stop_array)) |
368 | return 0; |
369 | |
370 | return 2; |
371 | } |
372 | |
373 | typedef struct |
374 | { |
375 | GtkCssImageRadial *self; |
376 | GArray *stop_array; |
377 | } ParseData; |
378 | |
379 | static guint |
380 | gtk_css_image_radial_parse_arg (GtkCssParser *parser, |
381 | guint arg, |
382 | gpointer user_data) |
383 | { |
384 | ParseData *parse_data = user_data; |
385 | GtkCssImageRadial *self = parse_data->self; |
386 | |
387 | if (arg == 0) |
388 | return gtk_css_image_radial_parse_first_arg (radial: self, parser, stop_array: parse_data->stop_array); |
389 | else |
390 | return gtk_css_image_radial_parse_color_stop (radial: self, parser, stop_array: parse_data->stop_array); |
391 | |
392 | } |
393 | |
394 | static gboolean |
395 | gtk_css_image_radial_parse (GtkCssImage *image, |
396 | GtkCssParser *parser) |
397 | { |
398 | GtkCssImageRadial *self = GTK_CSS_IMAGE_RADIAL (image); |
399 | ParseData parse_data; |
400 | gboolean success; |
401 | |
402 | if (gtk_css_parser_has_function (self: parser, name: "repeating-radial-gradient" )) |
403 | self->repeating = TRUE; |
404 | else if (gtk_css_parser_has_function (self: parser, name: "radial-gradient" )) |
405 | self->repeating = FALSE; |
406 | else |
407 | { |
408 | gtk_css_parser_error_syntax (self: parser, format: "Not a radial gradient" ); |
409 | return FALSE; |
410 | } |
411 | |
412 | parse_data.self = self; |
413 | parse_data.stop_array = g_array_new (TRUE, FALSE, element_size: sizeof (GtkCssImageRadialColorStop)); |
414 | |
415 | success = gtk_css_parser_consume_function (self: parser, min_args: 3, G_MAXUINT, parse_func: gtk_css_image_radial_parse_arg, data: &parse_data); |
416 | |
417 | if (!success) |
418 | { |
419 | g_array_free (array: parse_data.stop_array, TRUE); |
420 | } |
421 | else |
422 | { |
423 | self->n_stops = parse_data.stop_array->len; |
424 | self->color_stops = (GtkCssImageRadialColorStop *)g_array_free (array: parse_data.stop_array, FALSE); |
425 | } |
426 | |
427 | return success; |
428 | } |
429 | |
430 | static void |
431 | gtk_css_image_radial_print (GtkCssImage *image, |
432 | GString *string) |
433 | { |
434 | GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); |
435 | guint i; |
436 | const char *names[] = { |
437 | NULL, |
438 | "closest-side" , |
439 | "farthest-side" , |
440 | "closest-corner" , |
441 | "farthest-corner" |
442 | }; |
443 | |
444 | if (radial->repeating) |
445 | g_string_append (string, val: "repeating-radial-gradient(" ); |
446 | else |
447 | g_string_append (string, val: "radial-gradient(" ); |
448 | |
449 | if (radial->circle) |
450 | g_string_append (string, val: "circle " ); |
451 | else |
452 | g_string_append (string, val: "ellipse " ); |
453 | |
454 | if (radial->size != 0) |
455 | g_string_append (string, val: names[radial->size]); |
456 | else |
457 | { |
458 | if (radial->sizes[0]) |
459 | _gtk_css_value_print (value: radial->sizes[0], string); |
460 | if (radial->sizes[1]) |
461 | { |
462 | g_string_append (string, val: " " ); |
463 | _gtk_css_value_print (value: radial->sizes[1], string); |
464 | } |
465 | } |
466 | |
467 | g_string_append (string, val: " at " ); |
468 | _gtk_css_value_print (value: radial->position, string); |
469 | |
470 | g_string_append (string, val: ", " ); |
471 | |
472 | for (i = 0; i < radial->n_stops; i++) |
473 | { |
474 | const GtkCssImageRadialColorStop *stop = &radial->color_stops[i]; |
475 | |
476 | if (i > 0) |
477 | g_string_append (string, val: ", " ); |
478 | |
479 | _gtk_css_value_print (value: stop->color, string); |
480 | |
481 | if (stop->offset) |
482 | { |
483 | g_string_append (string, val: " " ); |
484 | _gtk_css_value_print (value: stop->offset, string); |
485 | } |
486 | } |
487 | |
488 | g_string_append (string, val: ")" ); |
489 | } |
490 | |
491 | static GtkCssImage * |
492 | gtk_css_image_radial_compute (GtkCssImage *image, |
493 | guint property_id, |
494 | GtkStyleProvider *provider, |
495 | GtkCssStyle *style, |
496 | GtkCssStyle *parent_style) |
497 | { |
498 | GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); |
499 | GtkCssImageRadial *copy; |
500 | guint i; |
501 | |
502 | copy = g_object_new (GTK_TYPE_CSS_IMAGE_RADIAL, NULL); |
503 | copy->repeating = radial->repeating; |
504 | copy->circle = radial->circle; |
505 | copy->size = radial->size; |
506 | |
507 | copy->position = _gtk_css_value_compute (value: radial->position, property_id, provider, style, parent_style); |
508 | |
509 | if (radial->sizes[0]) |
510 | copy->sizes[0] = _gtk_css_value_compute (value: radial->sizes[0], property_id, provider, style, parent_style); |
511 | |
512 | if (radial->sizes[1]) |
513 | copy->sizes[1] = _gtk_css_value_compute (value: radial->sizes[1], property_id, provider, style, parent_style); |
514 | |
515 | copy->n_stops = radial->n_stops; |
516 | copy->color_stops = g_malloc (n_bytes: sizeof (GtkCssImageRadialColorStop) * copy->n_stops); |
517 | for (i = 0; i < radial->n_stops; i++) |
518 | { |
519 | const GtkCssImageRadialColorStop *stop = &radial->color_stops[i]; |
520 | GtkCssImageRadialColorStop *scopy = ©->color_stops[i]; |
521 | |
522 | scopy->color = _gtk_css_value_compute (value: stop->color, property_id, provider, style, parent_style); |
523 | |
524 | if (stop->offset) |
525 | { |
526 | scopy->offset = _gtk_css_value_compute (value: stop->offset, property_id, provider, style, parent_style); |
527 | } |
528 | else |
529 | { |
530 | scopy->offset = NULL; |
531 | } |
532 | } |
533 | |
534 | return GTK_CSS_IMAGE (copy); |
535 | } |
536 | |
537 | static GtkCssImage * |
538 | gtk_css_image_radial_transition (GtkCssImage *start_image, |
539 | GtkCssImage *end_image, |
540 | guint property_id, |
541 | double progress) |
542 | { |
543 | GtkCssImageRadial *start, *end, *result; |
544 | guint i; |
545 | |
546 | start = GTK_CSS_IMAGE_RADIAL (start_image); |
547 | |
548 | if (end_image == NULL) |
549 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress); |
550 | |
551 | if (!GTK_IS_CSS_IMAGE_RADIAL (end_image)) |
552 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress); |
553 | |
554 | end = GTK_CSS_IMAGE_RADIAL (end_image); |
555 | |
556 | if (start->repeating != end->repeating || |
557 | start->n_stops != end->n_stops || |
558 | start->size != end->size || |
559 | start->circle != end->circle) |
560 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress); |
561 | |
562 | result = g_object_new (GTK_TYPE_CSS_IMAGE_RADIAL, NULL); |
563 | result->repeating = start->repeating; |
564 | result->circle = start->circle; |
565 | result->size = start->size; |
566 | |
567 | result->position = _gtk_css_value_transition (start: start->position, end: end->position, property_id, progress); |
568 | if (result->position == NULL) |
569 | goto fail; |
570 | |
571 | if (start->sizes[0] && end->sizes[0]) |
572 | { |
573 | result->sizes[0] = _gtk_css_value_transition (start: start->sizes[0], end: end->sizes[0], property_id, progress); |
574 | if (result->sizes[0] == NULL) |
575 | goto fail; |
576 | } |
577 | else |
578 | result->sizes[0] = 0; |
579 | |
580 | if (start->sizes[1] && end->sizes[1]) |
581 | { |
582 | result->sizes[1] = _gtk_css_value_transition (start: start->sizes[1], end: end->sizes[1], property_id, progress); |
583 | if (result->sizes[1] == NULL) |
584 | goto fail; |
585 | } |
586 | else |
587 | result->sizes[1] = 0; |
588 | |
589 | result->color_stops = g_malloc (n_bytes: sizeof (GtkCssImageRadialColorStop) * start->n_stops); |
590 | result->n_stops = 0; |
591 | for (i = 0; i < start->n_stops; i++) |
592 | { |
593 | const GtkCssImageRadialColorStop *start_stop = &start->color_stops[i]; |
594 | const GtkCssImageRadialColorStop *end_stop = &end->color_stops[i]; |
595 | GtkCssImageRadialColorStop *stop = &result->color_stops[i]; |
596 | |
597 | if ((start_stop->offset != NULL) != (end_stop->offset != NULL)) |
598 | goto fail; |
599 | |
600 | if (start_stop->offset == NULL) |
601 | { |
602 | stop->offset = NULL; |
603 | } |
604 | else |
605 | { |
606 | stop->offset = _gtk_css_value_transition (start: start_stop->offset, |
607 | end: end_stop->offset, |
608 | property_id, |
609 | progress); |
610 | if (stop->offset == NULL) |
611 | goto fail; |
612 | } |
613 | |
614 | stop->color = _gtk_css_value_transition (start: start_stop->color, |
615 | end: end_stop->color, |
616 | property_id, |
617 | progress); |
618 | if (stop->color == NULL) |
619 | { |
620 | if (stop->offset) |
621 | _gtk_css_value_unref (value: stop->offset); |
622 | goto fail; |
623 | } |
624 | |
625 | result->n_stops++; |
626 | } |
627 | |
628 | return GTK_CSS_IMAGE (result); |
629 | |
630 | fail: |
631 | g_object_unref (object: result); |
632 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_radial_parent_class)->transition (start_image, end_image, property_id, progress); |
633 | } |
634 | |
635 | static gboolean |
636 | gtk_css_image_radial_equal (GtkCssImage *image1, |
637 | GtkCssImage *image2) |
638 | { |
639 | GtkCssImageRadial *radial1 = (GtkCssImageRadial *) image1; |
640 | GtkCssImageRadial *radial2 = (GtkCssImageRadial *) image2; |
641 | guint i; |
642 | |
643 | if (radial1->repeating != radial2->repeating || |
644 | radial1->size != radial2->size || |
645 | !_gtk_css_value_equal (value1: radial1->position, value2: radial2->position) || |
646 | ((radial1->sizes[0] == NULL) != (radial2->sizes[0] == NULL)) || |
647 | (radial1->sizes[0] && radial2->sizes[0] && !_gtk_css_value_equal (value1: radial1->sizes[0], value2: radial2->sizes[0])) || |
648 | ((radial1->sizes[1] == NULL) != (radial2->sizes[1] == NULL)) || |
649 | (radial1->sizes[1] && radial2->sizes[1] && !_gtk_css_value_equal (value1: radial1->sizes[1], value2: radial2->sizes[1])) || |
650 | radial1->n_stops != radial2->n_stops) |
651 | return FALSE; |
652 | |
653 | for (i = 0; i < radial1->n_stops; i++) |
654 | { |
655 | const GtkCssImageRadialColorStop *stop1 = &radial1->color_stops[i]; |
656 | const GtkCssImageRadialColorStop *stop2 = &radial2->color_stops[i]; |
657 | |
658 | if (!_gtk_css_value_equal0 (value1: stop1->offset, value2: stop2->offset) || |
659 | !_gtk_css_value_equal (value1: stop1->color, value2: stop2->color)) |
660 | return FALSE; |
661 | } |
662 | |
663 | return TRUE; |
664 | } |
665 | |
666 | static void |
667 | gtk_css_image_radial_dispose (GObject *object) |
668 | { |
669 | GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (object); |
670 | guint i; |
671 | |
672 | for (i = 0; i < radial->n_stops; i ++) |
673 | { |
674 | GtkCssImageRadialColorStop *stop = &radial->color_stops[i]; |
675 | |
676 | _gtk_css_value_unref (value: stop->color); |
677 | if (stop->offset) |
678 | _gtk_css_value_unref (value: stop->offset); |
679 | } |
680 | g_free (mem: radial->color_stops); |
681 | |
682 | if (radial->position) |
683 | { |
684 | _gtk_css_value_unref (value: radial->position); |
685 | radial->position = NULL; |
686 | } |
687 | |
688 | for (i = 0; i < 2; i++) |
689 | if (radial->sizes[i]) |
690 | { |
691 | _gtk_css_value_unref (value: radial->sizes[i]); |
692 | radial->sizes[i] = NULL; |
693 | } |
694 | |
695 | G_OBJECT_CLASS (_gtk_css_image_radial_parent_class)->dispose (object); |
696 | } |
697 | |
698 | static gboolean |
699 | gtk_css_image_radial_is_computed (GtkCssImage *image) |
700 | { |
701 | GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); |
702 | guint i; |
703 | gboolean computed = TRUE; |
704 | |
705 | computed = computed && (!radial->position || gtk_css_value_is_computed (value: radial->position)); |
706 | computed = computed && (!radial->sizes[0] || gtk_css_value_is_computed (value: radial->sizes[0])); |
707 | computed = computed && (!radial->sizes[1] || gtk_css_value_is_computed (value: radial->sizes[1])); |
708 | |
709 | if (computed) |
710 | for (i = 0; i < radial->n_stops; i ++) |
711 | { |
712 | const GtkCssImageRadialColorStop *stop = &radial->color_stops[i]; |
713 | |
714 | if (stop->offset && !gtk_css_value_is_computed (value: stop->offset)) |
715 | { |
716 | computed = FALSE; |
717 | break; |
718 | } |
719 | |
720 | if (!gtk_css_value_is_computed (value: stop->color)) |
721 | { |
722 | computed = FALSE; |
723 | break; |
724 | } |
725 | } |
726 | |
727 | return computed; |
728 | } |
729 | static void |
730 | _gtk_css_image_radial_class_init (GtkCssImageRadialClass *klass) |
731 | { |
732 | GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass); |
733 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
734 | |
735 | image_class->snapshot = gtk_css_image_radial_snapshot; |
736 | image_class->parse = gtk_css_image_radial_parse; |
737 | image_class->print = gtk_css_image_radial_print; |
738 | image_class->compute = gtk_css_image_radial_compute; |
739 | image_class->transition = gtk_css_image_radial_transition; |
740 | image_class->equal = gtk_css_image_radial_equal; |
741 | image_class->is_computed = gtk_css_image_radial_is_computed; |
742 | |
743 | object_class->dispose = gtk_css_image_radial_dispose; |
744 | } |
745 | |
746 | static void |
747 | _gtk_css_image_radial_init (GtkCssImageRadial *radial) |
748 | { |
749 | } |
750 | |
751 | |