1 | /* |
2 | * Copyright © 2012 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 "gtkcssimagelinearprivate.h" |
23 | |
24 | #include <math.h> |
25 | |
26 | #include "gtkcsscolorvalueprivate.h" |
27 | #include "gtkcssnumbervalueprivate.h" |
28 | #include "gtkcsscolorvalueprivate.h" |
29 | #include "gtkcssprovider.h" |
30 | |
31 | G_DEFINE_TYPE (GtkCssImageLinear, _gtk_css_image_linear, GTK_TYPE_CSS_IMAGE) |
32 | |
33 | static void |
34 | gtk_css_image_linear_get_repeating_start_end (GtkCssImageLinear *linear, |
35 | double length, |
36 | double *start, |
37 | double *end) |
38 | { |
39 | const GtkCssImageLinearColorStop *stop; |
40 | double pos; |
41 | guint i; |
42 | |
43 | g_assert (linear->repeating); |
44 | |
45 | stop = &linear->color_stops[0]; |
46 | if (stop->offset == NULL) |
47 | *start = 0; |
48 | else |
49 | *start = _gtk_css_number_value_get (number: stop->offset, one_hundred_percent: length) / length; |
50 | |
51 | *end = *start; |
52 | |
53 | for (i = 0; i < linear->n_stops; i++) |
54 | { |
55 | stop = &linear->color_stops[i]; |
56 | |
57 | if (stop->offset == NULL) |
58 | continue; |
59 | |
60 | pos = _gtk_css_number_value_get (number: stop->offset, one_hundred_percent: length) / length; |
61 | |
62 | *end = MAX (pos, *end); |
63 | } |
64 | |
65 | if (stop->offset == NULL) |
66 | *end = MAX (*end, 1.0); |
67 | } |
68 | |
69 | static void |
70 | gtk_css_image_linear_compute_start_point (double angle_in_degrees, |
71 | double width, |
72 | double height, |
73 | double *out_x, |
74 | double *out_y, |
75 | double *out_length) |
76 | { |
77 | double angle, c, slope, perpendicular; |
78 | double x, y, length; |
79 | |
80 | angle = fmod (x: angle_in_degrees, y: 360); |
81 | if (angle < 0) |
82 | angle += 360; |
83 | |
84 | if (angle == 0) |
85 | { |
86 | *out_x = 0; |
87 | *out_y = -height; |
88 | *out_length = height; |
89 | return; |
90 | } |
91 | else if (angle == 90) |
92 | { |
93 | *out_x = width; |
94 | *out_y = 0; |
95 | *out_length = width; |
96 | return; |
97 | } |
98 | else if (angle == 180) |
99 | { |
100 | *out_x = 0; |
101 | *out_y = height; |
102 | *out_length = height; |
103 | return; |
104 | } |
105 | else if (angle == 270) |
106 | { |
107 | *out_x = -width; |
108 | *out_y = 0; |
109 | *out_length = width; |
110 | return; |
111 | } |
112 | |
113 | /* The tan() is confusing because the angle is clockwise |
114 | * from 'to top' */ |
115 | perpendicular = tan (x: angle * G_PI / 180); |
116 | slope = -1 / perpendicular; |
117 | |
118 | if (angle > 180) |
119 | width = - width; |
120 | if (angle < 90 || angle > 270) |
121 | height = - height; |
122 | |
123 | /* Compute c (of y = mx + c) of perpendicular */ |
124 | c = height - perpendicular * width; |
125 | |
126 | x = c / (slope - perpendicular); |
127 | y = perpendicular * x + c; |
128 | |
129 | length = sqrt (x: x * x + y * y); |
130 | |
131 | *out_x = x; |
132 | *out_y = y; |
133 | *out_length = length; |
134 | } |
135 | |
136 | static void |
137 | gtk_css_image_linear_snapshot (GtkCssImage *image, |
138 | GtkSnapshot *snapshot, |
139 | double width, |
140 | double height) |
141 | { |
142 | GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); |
143 | GskColorStop *stops; |
144 | double angle; /* actual angle of the gradiant line in degrees */ |
145 | double x, y; /* coordinates of start point */ |
146 | double length; /* distance in pixels for 100% */ |
147 | double start, end; /* position of first/last point on gradient line - with gradient line being [0, 1] */ |
148 | double offset; |
149 | int i, last; |
150 | |
151 | if (linear->side) |
152 | { |
153 | /* special casing the regular cases here so we don't get rounding errors */ |
154 | switch (linear->side) |
155 | { |
156 | case 1 << GTK_CSS_RIGHT: |
157 | angle = 90; |
158 | break; |
159 | case 1 << GTK_CSS_LEFT: |
160 | angle = 270; |
161 | break; |
162 | case 1 << GTK_CSS_TOP: |
163 | angle = 0; |
164 | break; |
165 | case 1 << GTK_CSS_BOTTOM: |
166 | angle = 180; |
167 | break; |
168 | default: |
169 | angle = atan2 (y: linear->side & 1 << GTK_CSS_TOP ? -width : width, |
170 | x: linear->side & 1 << GTK_CSS_LEFT ? -height : height); |
171 | angle = 180 * angle / G_PI + 90; |
172 | break; |
173 | } |
174 | } |
175 | else |
176 | { |
177 | angle = _gtk_css_number_value_get (number: linear->angle, one_hundred_percent: 100); |
178 | } |
179 | |
180 | gtk_css_image_linear_compute_start_point (angle_in_degrees: angle, |
181 | width, height, |
182 | out_x: &x, out_y: &y, |
183 | out_length: &length); |
184 | |
185 | if (linear->repeating) |
186 | { |
187 | gtk_css_image_linear_get_repeating_start_end (linear, length, start: &start, end: &end); |
188 | |
189 | if (start == end) |
190 | { |
191 | /* repeating gradients with all color stops sharing the same offset |
192 | * get the color of the last color stop */ |
193 | const GtkCssImageLinearColorStop *stop = &linear->color_stops[linear->n_stops - 1]; |
194 | |
195 | gtk_snapshot_append_color (snapshot, |
196 | color: gtk_css_color_value_get_rgba (color: stop->color), |
197 | bounds: &GRAPHENE_RECT_INIT (0, 0, width, height)); |
198 | return; |
199 | } |
200 | } |
201 | else |
202 | { |
203 | start = 0; |
204 | end = 1; |
205 | } |
206 | |
207 | offset = start; |
208 | last = -1; |
209 | stops = g_newa (GskColorStop, linear->n_stops); |
210 | |
211 | for (i = 0; i < linear->n_stops; i++) |
212 | { |
213 | const GtkCssImageLinearColorStop *stop = &linear->color_stops[i]; |
214 | double pos, step; |
215 | |
216 | if (stop->offset == NULL) |
217 | { |
218 | if (i == 0) |
219 | pos = 0.0; |
220 | else if (i + 1 == linear->n_stops) |
221 | pos = 1.0; |
222 | else |
223 | continue; |
224 | } |
225 | else |
226 | { |
227 | pos = _gtk_css_number_value_get (number: stop->offset, one_hundred_percent: length) / length; |
228 | pos = CLAMP (pos, 0.0, 1.0); |
229 | } |
230 | |
231 | pos = MAX (pos, offset); |
232 | step = (pos - offset) / (i - last); |
233 | for (last = last + 1; last <= i; last++) |
234 | { |
235 | stop = &linear->color_stops[last]; |
236 | |
237 | offset += step; |
238 | |
239 | stops[last].offset = (offset - start) / (end - start); |
240 | stops[last].color = *gtk_css_color_value_get_rgba (color: stop->color); |
241 | } |
242 | |
243 | offset = pos; |
244 | last = i; |
245 | } |
246 | |
247 | if (linear->repeating) |
248 | { |
249 | gtk_snapshot_append_repeating_linear_gradient ( |
250 | snapshot, |
251 | bounds: &GRAPHENE_RECT_INIT (0, 0, width, height), |
252 | start_point: &GRAPHENE_POINT_INIT (width / 2 + x * (start - 0.5), height / 2 + y * (start - 0.5)), |
253 | end_point: &GRAPHENE_POINT_INIT (width / 2 + x * (end - 0.5), height / 2 + y * (end - 0.5)), |
254 | stops, |
255 | n_stops: linear->n_stops); |
256 | } |
257 | else |
258 | { |
259 | gtk_snapshot_append_linear_gradient ( |
260 | snapshot, |
261 | bounds: &GRAPHENE_RECT_INIT (0, 0, width, height), |
262 | start_point: &GRAPHENE_POINT_INIT (width / 2 + x * (start - 0.5), height / 2 + y * (start - 0.5)), |
263 | end_point: &GRAPHENE_POINT_INIT (width / 2 + x * (end - 0.5), height / 2 + y * (end - 0.5)), |
264 | stops, |
265 | n_stops: linear->n_stops); |
266 | } |
267 | } |
268 | |
269 | static guint |
270 | gtk_css_image_linear_parse_color_stop (GtkCssImageLinear *self, |
271 | GtkCssParser *parser, |
272 | GArray *stop_array) |
273 | { |
274 | GtkCssImageLinearColorStop stop; |
275 | |
276 | stop.color = _gtk_css_color_value_parse (parser); |
277 | if (stop.color == NULL) |
278 | return 0; |
279 | |
280 | if (gtk_css_number_value_can_parse (parser)) |
281 | { |
282 | stop.offset = _gtk_css_number_value_parse (parser, |
283 | flags: GTK_CSS_PARSE_PERCENT |
284 | | GTK_CSS_PARSE_LENGTH); |
285 | if (stop.offset == NULL) |
286 | { |
287 | _gtk_css_value_unref (value: stop.color); |
288 | return 0; |
289 | } |
290 | } |
291 | else |
292 | { |
293 | stop.offset = NULL; |
294 | } |
295 | |
296 | g_array_append_val (stop_array, stop); |
297 | |
298 | return 1; |
299 | } |
300 | |
301 | static guint |
302 | gtk_css_image_linear_parse_first_arg (GtkCssImageLinear *linear, |
303 | GtkCssParser *parser, |
304 | GArray *stop_array) |
305 | { |
306 | guint i; |
307 | |
308 | if (gtk_css_parser_try_ident (self: parser, ident: "to" )) |
309 | { |
310 | for (i = 0; i < 2; i++) |
311 | { |
312 | if (gtk_css_parser_try_ident (self: parser, ident: "left" )) |
313 | { |
314 | if (linear->side & ((1 << GTK_CSS_LEFT) | (1 << GTK_CSS_RIGHT))) |
315 | { |
316 | gtk_css_parser_error_syntax (self: parser, format: "Expected 'top', 'bottom' or comma" ); |
317 | return 0; |
318 | } |
319 | linear->side |= (1 << GTK_CSS_LEFT); |
320 | } |
321 | else if (gtk_css_parser_try_ident (self: parser, ident: "right" )) |
322 | { |
323 | if (linear->side & ((1 << GTK_CSS_LEFT) | (1 << GTK_CSS_RIGHT))) |
324 | { |
325 | gtk_css_parser_error_syntax (self: parser, format: "Expected 'top', 'bottom' or comma" ); |
326 | return 0; |
327 | } |
328 | linear->side |= (1 << GTK_CSS_RIGHT); |
329 | } |
330 | else if (gtk_css_parser_try_ident (self: parser, ident: "top" )) |
331 | { |
332 | if (linear->side & ((1 << GTK_CSS_TOP) | (1 << GTK_CSS_BOTTOM))) |
333 | { |
334 | gtk_css_parser_error_syntax (self: parser, format: "Expected 'left', 'right' or comma" ); |
335 | return 0; |
336 | } |
337 | linear->side |= (1 << GTK_CSS_TOP); |
338 | } |
339 | else if (gtk_css_parser_try_ident (self: parser, ident: "bottom" )) |
340 | { |
341 | if (linear->side & ((1 << GTK_CSS_TOP) | (1 << GTK_CSS_BOTTOM))) |
342 | { |
343 | gtk_css_parser_error_syntax (self: parser, format: "Expected 'left', 'right' or comma" ); |
344 | return 0; |
345 | } |
346 | linear->side |= (1 << GTK_CSS_BOTTOM); |
347 | } |
348 | else |
349 | break; |
350 | } |
351 | |
352 | if (linear->side == 0) |
353 | { |
354 | gtk_css_parser_error_syntax (self: parser, format: "Expected side that gradient should go to" ); |
355 | return 0; |
356 | } |
357 | |
358 | return 1; |
359 | } |
360 | else if (gtk_css_number_value_can_parse (parser)) |
361 | { |
362 | linear->angle = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_ANGLE); |
363 | if (linear->angle == NULL) |
364 | return 0; |
365 | |
366 | return 1; |
367 | } |
368 | else |
369 | { |
370 | linear->side = 1 << GTK_CSS_BOTTOM; |
371 | if (!gtk_css_image_linear_parse_color_stop (self: linear, parser, stop_array)) |
372 | return 0; |
373 | |
374 | return 2; |
375 | } |
376 | } |
377 | |
378 | typedef struct |
379 | { |
380 | GtkCssImageLinear *self; |
381 | GArray *stop_array; |
382 | } ParseData; |
383 | |
384 | static guint |
385 | gtk_css_image_linear_parse_arg (GtkCssParser *parser, |
386 | guint arg, |
387 | gpointer user_data) |
388 | { |
389 | ParseData *parse_data = user_data; |
390 | GtkCssImageLinear *self = parse_data->self; |
391 | |
392 | if (arg == 0) |
393 | return gtk_css_image_linear_parse_first_arg (linear: self, parser, stop_array: parse_data->stop_array); |
394 | else |
395 | return gtk_css_image_linear_parse_color_stop (self, parser, stop_array: parse_data->stop_array); |
396 | } |
397 | |
398 | static gboolean |
399 | gtk_css_image_linear_parse (GtkCssImage *image, |
400 | GtkCssParser *parser) |
401 | { |
402 | GtkCssImageLinear *self = GTK_CSS_IMAGE_LINEAR (image); |
403 | ParseData parse_data; |
404 | gboolean success; |
405 | |
406 | if (gtk_css_parser_has_function (self: parser, name: "repeating-linear-gradient" )) |
407 | self->repeating = TRUE; |
408 | else if (gtk_css_parser_has_function (self: parser, name: "linear-gradient" )) |
409 | self->repeating = FALSE; |
410 | else |
411 | { |
412 | gtk_css_parser_error_syntax (self: parser, format: "Not a linear gradient" ); |
413 | return FALSE; |
414 | } |
415 | |
416 | parse_data.self = self; |
417 | parse_data.stop_array = g_array_new (TRUE, FALSE, element_size: sizeof (GtkCssImageLinearColorStop)); |
418 | |
419 | success = gtk_css_parser_consume_function (self: parser, min_args: 3, G_MAXUINT, parse_func: gtk_css_image_linear_parse_arg, data: &parse_data); |
420 | |
421 | if (!success) |
422 | { |
423 | g_array_free (array: parse_data.stop_array, TRUE); |
424 | } |
425 | else |
426 | { |
427 | self->n_stops = parse_data.stop_array->len; |
428 | self->color_stops = (GtkCssImageLinearColorStop *)g_array_free (array: parse_data.stop_array, FALSE); |
429 | } |
430 | |
431 | return success; |
432 | } |
433 | |
434 | static void |
435 | gtk_css_image_linear_print (GtkCssImage *image, |
436 | GString *string) |
437 | { |
438 | GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); |
439 | guint i; |
440 | |
441 | if (linear->repeating) |
442 | g_string_append (string, val: "repeating-linear-gradient(" ); |
443 | else |
444 | g_string_append (string, val: "linear-gradient(" ); |
445 | |
446 | if (linear->side) |
447 | { |
448 | if (linear->side != (1 << GTK_CSS_BOTTOM)) |
449 | { |
450 | g_string_append (string, val: "to" ); |
451 | |
452 | if (linear->side & (1 << GTK_CSS_TOP)) |
453 | g_string_append (string, val: " top" ); |
454 | else if (linear->side & (1 << GTK_CSS_BOTTOM)) |
455 | g_string_append (string, val: " bottom" ); |
456 | |
457 | if (linear->side & (1 << GTK_CSS_LEFT)) |
458 | g_string_append (string, val: " left" ); |
459 | else if (linear->side & (1 << GTK_CSS_RIGHT)) |
460 | g_string_append (string, val: " right" ); |
461 | |
462 | g_string_append (string, val: ", " ); |
463 | } |
464 | } |
465 | else |
466 | { |
467 | _gtk_css_value_print (value: linear->angle, string); |
468 | g_string_append (string, val: ", " ); |
469 | } |
470 | |
471 | for (i = 0; i < linear->n_stops; i++) |
472 | { |
473 | const GtkCssImageLinearColorStop *stop = &linear->color_stops[i]; |
474 | |
475 | if (i > 0) |
476 | g_string_append (string, val: ", " ); |
477 | |
478 | _gtk_css_value_print (value: stop->color, string); |
479 | |
480 | if (stop->offset) |
481 | { |
482 | g_string_append (string, val: " " ); |
483 | _gtk_css_value_print (value: stop->offset, string); |
484 | } |
485 | } |
486 | |
487 | g_string_append (string, val: ")" ); |
488 | } |
489 | |
490 | static GtkCssImage * |
491 | gtk_css_image_linear_compute (GtkCssImage *image, |
492 | guint property_id, |
493 | GtkStyleProvider *provider, |
494 | GtkCssStyle *style, |
495 | GtkCssStyle *parent_style) |
496 | { |
497 | GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); |
498 | GtkCssImageLinear *copy; |
499 | guint i; |
500 | |
501 | copy = g_object_new (GTK_TYPE_CSS_IMAGE_LINEAR, NULL); |
502 | copy->repeating = linear->repeating; |
503 | copy->side = linear->side; |
504 | |
505 | if (linear->angle) |
506 | copy->angle = _gtk_css_value_compute (value: linear->angle, property_id, provider, style, parent_style); |
507 | |
508 | copy->n_stops = linear->n_stops; |
509 | copy->color_stops = g_malloc (n_bytes: sizeof (GtkCssImageLinearColorStop) * copy->n_stops); |
510 | for (i = 0; i < linear->n_stops; i++) |
511 | { |
512 | const GtkCssImageLinearColorStop *stop = &linear->color_stops[i]; |
513 | GtkCssImageLinearColorStop *scopy = ©->color_stops[i]; |
514 | |
515 | scopy->color = _gtk_css_value_compute (value: stop->color, property_id, provider, style, parent_style); |
516 | |
517 | if (stop->offset) |
518 | { |
519 | scopy->offset = _gtk_css_value_compute (value: stop->offset, property_id, provider, style, parent_style); |
520 | } |
521 | else |
522 | { |
523 | scopy->offset = NULL; |
524 | } |
525 | } |
526 | |
527 | return GTK_CSS_IMAGE (copy); |
528 | } |
529 | |
530 | static GtkCssImage * |
531 | gtk_css_image_linear_transition (GtkCssImage *start_image, |
532 | GtkCssImage *end_image, |
533 | guint property_id, |
534 | double progress) |
535 | { |
536 | GtkCssImageLinear *start, *end, *result; |
537 | guint i; |
538 | |
539 | start = GTK_CSS_IMAGE_LINEAR (start_image); |
540 | |
541 | if (end_image == NULL) |
542 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_linear_parent_class)->transition (start_image, end_image, property_id, progress); |
543 | |
544 | if (!GTK_IS_CSS_IMAGE_LINEAR (end_image)) |
545 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_linear_parent_class)->transition (start_image, end_image, property_id, progress); |
546 | |
547 | end = GTK_CSS_IMAGE_LINEAR (end_image); |
548 | |
549 | if ((start->repeating != end->repeating) |
550 | || (start->n_stops != end->n_stops)) |
551 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_linear_parent_class)->transition (start_image, end_image, property_id, progress); |
552 | |
553 | result = g_object_new (GTK_TYPE_CSS_IMAGE_LINEAR, NULL); |
554 | result->repeating = start->repeating; |
555 | |
556 | if (start->side != end->side) |
557 | goto fail; |
558 | |
559 | result->side = start->side; |
560 | if (result->side == 0) |
561 | result->angle = _gtk_css_value_transition (start: start->angle, end: end->angle, property_id, progress); |
562 | if (result->angle == NULL) |
563 | goto fail; |
564 | |
565 | /* Maximum amountof stops */ |
566 | result->color_stops = g_malloc (n_bytes: sizeof (GtkCssImageLinearColorStop) * start->n_stops); |
567 | result->n_stops = 0; |
568 | for (i = 0; i < start->n_stops; i++) |
569 | { |
570 | const GtkCssImageLinearColorStop *start_stop = &start->color_stops[i]; |
571 | const GtkCssImageLinearColorStop *end_stop = &end->color_stops[i]; |
572 | GtkCssImageLinearColorStop *stop = &result->color_stops[i]; |
573 | |
574 | if ((start_stop->offset != NULL) != (end_stop->offset != NULL)) |
575 | goto fail; |
576 | |
577 | if (start_stop->offset == NULL) |
578 | { |
579 | stop->offset = NULL; |
580 | } |
581 | else |
582 | { |
583 | stop->offset = _gtk_css_value_transition (start: start_stop->offset, |
584 | end: end_stop->offset, |
585 | property_id, |
586 | progress); |
587 | if (stop->offset == NULL) |
588 | goto fail; |
589 | } |
590 | |
591 | stop->color = _gtk_css_value_transition (start: start_stop->color, |
592 | end: end_stop->color, |
593 | property_id, |
594 | progress); |
595 | if (stop->color == NULL) |
596 | { |
597 | if (stop->offset) |
598 | _gtk_css_value_unref (value: stop->offset); |
599 | goto fail; |
600 | } |
601 | |
602 | result->n_stops ++; |
603 | } |
604 | |
605 | return GTK_CSS_IMAGE (result); |
606 | |
607 | fail: |
608 | g_object_unref (object: result); |
609 | return GTK_CSS_IMAGE_CLASS (_gtk_css_image_linear_parent_class)->transition (start_image, end_image, property_id, progress); |
610 | } |
611 | |
612 | static gboolean |
613 | gtk_css_image_linear_equal (GtkCssImage *image1, |
614 | GtkCssImage *image2) |
615 | { |
616 | GtkCssImageLinear *linear1 = (GtkCssImageLinear *) image1; |
617 | GtkCssImageLinear *linear2 = (GtkCssImageLinear *) image2; |
618 | guint i; |
619 | |
620 | if (linear1->repeating != linear2->repeating || |
621 | linear1->side != linear2->side || |
622 | (linear1->side == 0 && !_gtk_css_value_equal (value1: linear1->angle, value2: linear2->angle)) || |
623 | linear1->n_stops != linear2->n_stops) |
624 | return FALSE; |
625 | |
626 | for (i = 0; i < linear1->n_stops; i++) |
627 | { |
628 | const GtkCssImageLinearColorStop *stop1 = &linear1->color_stops[i]; |
629 | const GtkCssImageLinearColorStop *stop2 = &linear2->color_stops[i]; |
630 | |
631 | if (!_gtk_css_value_equal0 (value1: stop1->offset, value2: stop2->offset) || |
632 | !_gtk_css_value_equal (value1: stop1->color, value2: stop2->color)) |
633 | return FALSE; |
634 | } |
635 | |
636 | return TRUE; |
637 | } |
638 | |
639 | static void |
640 | gtk_css_image_linear_dispose (GObject *object) |
641 | { |
642 | GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (object); |
643 | guint i; |
644 | |
645 | for (i = 0; i < linear->n_stops; i ++) |
646 | { |
647 | GtkCssImageLinearColorStop *stop = &linear->color_stops[i]; |
648 | |
649 | _gtk_css_value_unref (value: stop->color); |
650 | if (stop->offset) |
651 | _gtk_css_value_unref (value: stop->offset); |
652 | } |
653 | g_free (mem: linear->color_stops); |
654 | |
655 | linear->side = 0; |
656 | if (linear->angle) |
657 | { |
658 | _gtk_css_value_unref (value: linear->angle); |
659 | linear->angle = NULL; |
660 | } |
661 | |
662 | G_OBJECT_CLASS (_gtk_css_image_linear_parent_class)->dispose (object); |
663 | } |
664 | |
665 | static gboolean |
666 | gtk_css_image_linear_is_computed (GtkCssImage *image) |
667 | { |
668 | GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); |
669 | guint i; |
670 | gboolean computed = TRUE; |
671 | |
672 | computed = !linear->angle || gtk_css_value_is_computed (value: linear->angle); |
673 | |
674 | for (i = 0; i < linear->n_stops; i ++) |
675 | { |
676 | const GtkCssImageLinearColorStop *stop = &linear->color_stops[i]; |
677 | |
678 | if (stop->offset && !gtk_css_value_is_computed (value: stop->offset)) |
679 | { |
680 | computed = FALSE; |
681 | break; |
682 | } |
683 | |
684 | if (!gtk_css_value_is_computed (value: stop->color)) |
685 | { |
686 | computed = FALSE; |
687 | break; |
688 | } |
689 | } |
690 | |
691 | return computed; |
692 | } |
693 | |
694 | static void |
695 | _gtk_css_image_linear_class_init (GtkCssImageLinearClass *klass) |
696 | { |
697 | GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass); |
698 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
699 | |
700 | image_class->snapshot = gtk_css_image_linear_snapshot; |
701 | image_class->parse = gtk_css_image_linear_parse; |
702 | image_class->print = gtk_css_image_linear_print; |
703 | image_class->compute = gtk_css_image_linear_compute; |
704 | image_class->equal = gtk_css_image_linear_equal; |
705 | image_class->transition = gtk_css_image_linear_transition; |
706 | image_class->is_computed = gtk_css_image_linear_is_computed; |
707 | |
708 | object_class->dispose = gtk_css_image_linear_dispose; |
709 | } |
710 | |
711 | static void |
712 | _gtk_css_image_linear_init (GtkCssImageLinear *linear) |
713 | { |
714 | } |
715 | |
716 | |