1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Red Hat, Inc.
3 *
4 * Author: Cosimo Cecchi <cosimoc@gnome.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "config.h"
21
22#include "gtkcssshadowvalueprivate.h"
23
24#include "gtkcsscolorvalueprivate.h"
25#include "gtkcssnumbervalueprivate.h"
26#include "gtkcsscolorvalueprivate.h"
27#include "gtksnapshotprivate.h"
28#include "gtkpango.h"
29
30#include "gsk/gskcairoblurprivate.h"
31#include "gsk/gskroundedrectprivate.h"
32
33#include <math.h>
34
35typedef struct {
36 guint inset :1;
37
38 GtkCssValue *hoffset;
39 GtkCssValue *voffset;
40 GtkCssValue *radius;
41 GtkCssValue *spread;
42 GtkCssValue *color;
43} ShadowValue;
44
45struct _GtkCssValue {
46 GTK_CSS_VALUE_BASE
47 guint is_filter : 1; /* values stored in radius are std_dev, for drop-shadow */
48 guint n_shadows;
49 ShadowValue shadows[1];
50};
51
52static GtkCssValue * gtk_css_shadow_value_new (ShadowValue *shadows,
53 guint n_shadows,
54 gboolean is_filter);
55
56static void
57shadow_value_for_transition (ShadowValue *result,
58 gboolean inset)
59{
60 result->inset = inset;
61 result->hoffset = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
62 result->voffset = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
63 result->radius = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
64 result->spread = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
65 result->color = gtk_css_color_value_new_transparent ();
66}
67
68static void
69shadow_value_unref (const ShadowValue *shadow)
70{
71 _gtk_css_value_unref (value: shadow->hoffset);
72 _gtk_css_value_unref (value: shadow->voffset);
73 _gtk_css_value_unref (value: shadow->spread);
74 _gtk_css_value_unref (value: shadow->radius);
75 _gtk_css_value_unref (value: shadow->color);
76}
77
78static gboolean
79shadow_value_transition (const ShadowValue *start,
80 const ShadowValue *end,
81 guint property_id,
82 double progress,
83 ShadowValue *result)
84
85{
86 if (start->inset != end->inset)
87 return FALSE;
88
89 result->inset = start->inset;
90 result->hoffset = _gtk_css_value_transition (start: start->hoffset, end: end->hoffset, property_id, progress);
91 result->voffset = _gtk_css_value_transition (start: start->voffset, end: end->voffset, property_id, progress);
92 result->radius = _gtk_css_value_transition (start: start->radius, end: end->radius, property_id, progress);
93 result->spread = _gtk_css_value_transition (start: start->spread, end: end->spread, property_id, progress);
94 result->color = _gtk_css_value_transition (start: start->color, end: end->color, property_id, progress);
95
96 return TRUE;
97}
98
99static void
100gtk_css_value_shadow_free (GtkCssValue *value)
101{
102 guint i;
103
104 for (i = 0; i < value->n_shadows; i ++)
105 {
106 const ShadowValue *shadow = &value->shadows[i];
107
108 shadow_value_unref (shadow);
109 }
110
111 g_slice_free1 (block_size: sizeof (GtkCssValue) + sizeof (ShadowValue) * (value->n_shadows - 1), mem_block: value);
112}
113
114static GtkCssValue *
115gtk_css_value_shadow_compute (GtkCssValue *value,
116 guint property_id,
117 GtkStyleProvider *provider,
118 GtkCssStyle *style,
119 GtkCssStyle *parent_style)
120{
121 guint i;
122 ShadowValue *shadows;
123
124 shadows = g_alloca (sizeof (ShadowValue) * value->n_shadows);
125
126 for (i = 0; i < value->n_shadows; i++)
127 {
128 const ShadowValue *shadow = &value->shadows[i];
129
130 shadows[i].hoffset = _gtk_css_value_compute (value: shadow->hoffset, property_id, provider, style, parent_style);
131 shadows[i].voffset = _gtk_css_value_compute (value: shadow->voffset, property_id, provider, style, parent_style);
132 shadows[i].radius = _gtk_css_value_compute (value: shadow->radius, property_id, provider, style, parent_style);
133 shadows[i].spread = _gtk_css_value_compute (value: shadow->spread, property_id, provider, style, parent_style),
134 shadows[i].color = _gtk_css_value_compute (value: shadow->color, property_id, provider, style, parent_style);
135 shadows[i].inset = shadow->inset;
136 }
137
138 return gtk_css_shadow_value_new (shadows, n_shadows: value->n_shadows, is_filter: value->is_filter);
139}
140
141static gboolean
142gtk_css_value_shadow_equal (const GtkCssValue *value1,
143 const GtkCssValue *value2)
144{
145 guint i;
146
147 if (value1->n_shadows != value2->n_shadows)
148 return FALSE;
149
150 for (i = 0; i < value1->n_shadows; i++)
151 {
152 const ShadowValue *shadow1 = &value1->shadows[i];
153 const ShadowValue *shadow2 = &value2->shadows[i];
154
155 if (shadow1->inset != shadow2->inset ||
156 !_gtk_css_value_equal (value1: shadow1->hoffset, value2: shadow2->hoffset) ||
157 !_gtk_css_value_equal (value1: shadow1->voffset, value2: shadow2->voffset) ||
158 !_gtk_css_value_equal (value1: shadow1->radius, value2: shadow2->radius) ||
159 !_gtk_css_value_equal (value1: shadow1->spread, value2: shadow2->spread) ||
160 !_gtk_css_value_equal (value1: shadow1->color, value2: shadow2->color))
161 return FALSE;
162 }
163
164 return TRUE;
165}
166
167static GtkCssValue *
168gtk_css_value_shadow_transition (GtkCssValue *start,
169 GtkCssValue *end,
170 guint property_id,
171 double progress)
172{
173
174 guint i, len;
175 ShadowValue *shadows;
176
177 if (start->n_shadows > end->n_shadows)
178 len = start->n_shadows;
179 else
180 len = end->n_shadows;
181
182 shadows = g_newa (ShadowValue, len);
183
184 for (i = 0; i < MIN (start->n_shadows, end->n_shadows); i++)
185 {
186 if (!shadow_value_transition (start: &start->shadows[i], end: &end->shadows[i],
187 property_id, progress,
188 result: &shadows[i]))
189 {
190 while (i--)
191 shadow_value_unref (shadow: &shadows[i]);
192
193 return NULL;
194 }
195 }
196
197 if (start->n_shadows > end->n_shadows)
198 {
199 for (; i < len; i++)
200 {
201 ShadowValue fill;
202
203 shadow_value_for_transition (result: &fill, inset: start->shadows[i].inset);
204 if (!shadow_value_transition (start: &start->shadows[i], end: &fill, property_id, progress, result: &shadows[i]))
205 {
206 while (i--)
207 shadow_value_unref (shadow: &shadows[i]);
208 shadow_value_unref (shadow: &fill);
209 return NULL;
210 }
211 shadow_value_unref (shadow: &fill);
212 }
213 }
214 else
215 {
216 for (; i < len; i++)
217 {
218 ShadowValue fill;
219
220 shadow_value_for_transition (result: &fill, inset: end->shadows[i].inset);
221 if (!shadow_value_transition (start: &fill, end: &end->shadows[i], property_id, progress, result: &shadows[i]))
222 {
223 while (i--)
224 shadow_value_unref (shadow: &shadows[i]);
225 shadow_value_unref (shadow: &fill);
226 return NULL;
227 }
228 shadow_value_unref (shadow: &fill);
229 }
230 }
231
232 return gtk_css_shadow_value_new (shadows, n_shadows: len, is_filter: start->is_filter);
233}
234
235static void
236gtk_css_value_shadow_print (const GtkCssValue *value,
237 GString *string)
238{
239 guint i;
240
241 if (value->n_shadows == 0)
242 {
243 g_string_append (string, val: "none");
244 return;
245 }
246
247 for (i = 0; i < value->n_shadows; i ++)
248 {
249 const ShadowValue *shadow = &value->shadows[i];
250
251 if (i > 0)
252 g_string_append (string, val: ", ");
253
254 _gtk_css_value_print (value: shadow->hoffset, string);
255 g_string_append_c (string, ' ');
256 _gtk_css_value_print (value: shadow->voffset, string);
257 g_string_append_c (string, ' ');
258 if (_gtk_css_number_value_get (number: shadow->radius, one_hundred_percent: 100) != 0 ||
259 _gtk_css_number_value_get (number: shadow->spread, one_hundred_percent: 100) != 0)
260 {
261 _gtk_css_value_print (value: shadow->radius, string);
262 g_string_append_c (string, ' ');
263 }
264
265 if (_gtk_css_number_value_get (number: shadow->spread, one_hundred_percent: 100) != 0)
266 {
267 _gtk_css_value_print (value: shadow->spread, string);
268 g_string_append_c (string, ' ');
269 }
270
271 _gtk_css_value_print (value: shadow->color, string);
272
273 if (shadow->inset)
274 g_string_append (string, val: " inset");
275 }
276}
277
278static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
279 "GtkCssShadowValue",
280 gtk_css_value_shadow_free,
281 gtk_css_value_shadow_compute,
282 gtk_css_value_shadow_equal,
283 gtk_css_value_shadow_transition,
284 NULL,
285 NULL,
286 gtk_css_value_shadow_print
287};
288
289static GtkCssValue shadow_none_singleton = { &GTK_CSS_VALUE_SHADOW, 1, TRUE, FALSE, 0 };
290
291GtkCssValue *
292gtk_css_shadow_value_new_none (void)
293{
294 return _gtk_css_value_ref (value: &shadow_none_singleton);
295}
296
297static GtkCssValue *
298gtk_css_shadow_value_new (ShadowValue *shadows,
299 guint n_shadows,
300 gboolean is_filter)
301{
302 GtkCssValue *retval;
303 guint i;
304
305 if (n_shadows == 0)
306 return gtk_css_shadow_value_new_none ();
307
308 retval = _gtk_css_value_alloc (klass: &GTK_CSS_VALUE_SHADOW, size: sizeof (GtkCssValue) + sizeof (ShadowValue) * (n_shadows - 1));
309 retval->n_shadows = n_shadows;
310 retval->is_filter = is_filter;
311
312 memcpy (dest: retval->shadows, src: shadows, n: sizeof (ShadowValue) * n_shadows);
313
314 retval->is_computed = TRUE;
315 for (i = 0; i < n_shadows; i++)
316 {
317 const ShadowValue *shadow = &retval->shadows[i];
318
319 if (!gtk_css_value_is_computed (value: shadow->hoffset) ||
320 !gtk_css_value_is_computed (value: shadow->voffset) ||
321 !gtk_css_value_is_computed (value: shadow->spread) ||
322 !gtk_css_value_is_computed (value: shadow->radius) ||
323 !gtk_css_value_is_computed (value: shadow->color))
324 {
325 retval->is_computed = FALSE;
326 break;
327 }
328 }
329
330 return retval;
331}
332
333GtkCssValue *
334gtk_css_shadow_value_new_filter (void)
335{
336 ShadowValue value;
337
338 value.inset = FALSE;
339 value.hoffset = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
340 value.voffset = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
341 value.radius = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
342 value.spread = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
343 value.color = _gtk_css_color_value_new_current_color ();
344
345 return gtk_css_shadow_value_new (shadows: &value, n_shadows: 1, TRUE);
346}
347
348enum {
349 HOFFSET,
350 VOFFSET,
351 RADIUS,
352 SPREAD,
353 N_VALUES
354};
355
356static gboolean
357has_inset (GtkCssParser *parser,
358 gpointer option_data,
359 gpointer box_shadow_mode)
360{
361 return box_shadow_mode && gtk_css_parser_has_ident (self: parser, ident: "inset");
362}
363
364static gboolean
365parse_inset (GtkCssParser *parser,
366 gpointer option_data,
367 gpointer box_shadow_mode)
368{
369 gboolean *inset = option_data;
370
371 if (!gtk_css_parser_try_ident (self: parser, ident: "inset"))
372 {
373 g_assert_not_reached ();
374 return FALSE;
375 }
376
377 *inset = TRUE;
378
379 return TRUE;
380}
381
382static gboolean
383parse_lengths (GtkCssParser *parser,
384 gpointer option_data,
385 gpointer box_shadow_mode)
386{
387 GtkCssValue **values = option_data;
388
389 values[HOFFSET] = _gtk_css_number_value_parse (parser,
390 flags: GTK_CSS_PARSE_LENGTH);
391 if (values[HOFFSET] == NULL)
392 return FALSE;
393
394 values[VOFFSET] = _gtk_css_number_value_parse (parser,
395 flags: GTK_CSS_PARSE_LENGTH);
396 if (values[VOFFSET] == NULL)
397 return FALSE;
398
399 if (gtk_css_number_value_can_parse (parser))
400 {
401 values[RADIUS] = _gtk_css_number_value_parse (parser,
402 flags: GTK_CSS_PARSE_LENGTH
403 | GTK_CSS_POSITIVE_ONLY);
404 if (values[RADIUS] == NULL)
405 return FALSE;
406 }
407 else
408 values[RADIUS] = _gtk_css_number_value_new (value: 0.0, unit: GTK_CSS_PX);
409
410 if (box_shadow_mode && gtk_css_number_value_can_parse (parser))
411 {
412 values[SPREAD] = _gtk_css_number_value_parse (parser,
413 flags: GTK_CSS_PARSE_LENGTH);
414 if (values[SPREAD] == NULL)
415 return FALSE;
416 }
417 else
418 values[SPREAD] = _gtk_css_number_value_new (value: 0.0, unit: GTK_CSS_PX);
419
420 return TRUE;
421}
422
423static gboolean
424parse_color (GtkCssParser *parser,
425 gpointer option_data,
426 gpointer box_shadow_mode)
427{
428 GtkCssValue **color = option_data;
429
430 *color = _gtk_css_color_value_parse (parser);
431 if (*color == NULL)
432 return FALSE;
433
434 return TRUE;
435}
436
437static gboolean
438gtk_css_shadow_value_parse_one (GtkCssParser *parser,
439 gboolean box_shadow_mode,
440 ShadowValue *result)
441{
442 GtkCssValue *values[N_VALUES] = { NULL, };
443 GtkCssValue *color = NULL;
444 gboolean inset = FALSE;
445 GtkCssParseOption options[] =
446 {
447 { (void *) gtk_css_number_value_can_parse, parse_lengths, values },
448 { has_inset, parse_inset, &inset },
449 { (void *) gtk_css_color_value_can_parse, parse_color, &color },
450 };
451 guint i;
452
453 if (!gtk_css_parser_consume_any (parser, options, G_N_ELEMENTS (options), GUINT_TO_POINTER (box_shadow_mode)))
454 goto fail;
455
456 if (values[0] == NULL)
457 {
458 gtk_css_parser_error_syntax (self: parser, format: "Expected shadow value to contain a length");
459 goto fail;
460 }
461
462 if (color == NULL)
463 color = _gtk_css_color_value_new_current_color ();
464
465 result->hoffset = values[HOFFSET];
466 result->voffset = values[VOFFSET];
467 result->spread = values[SPREAD];
468 result->radius = values[RADIUS];
469 result->color = color;
470 result->inset = inset;
471
472 return TRUE;
473
474fail:
475 for (i = 0; i < N_VALUES; i++)
476 {
477 g_clear_pointer (&values[i], gtk_css_value_unref);
478 }
479 g_clear_pointer (&color, gtk_css_value_unref);
480
481 return FALSE;
482}
483
484#define MAX_SHADOWS 64
485GtkCssValue *
486gtk_css_shadow_value_parse (GtkCssParser *parser,
487 gboolean box_shadow_mode)
488{
489 ShadowValue shadows[MAX_SHADOWS];
490 int n_shadows = 0;
491 int i;
492
493 if (gtk_css_parser_try_ident (self: parser, ident: "none"))
494 return gtk_css_shadow_value_new_none ();
495
496 do {
497 if (n_shadows == MAX_SHADOWS)
498 {
499 gtk_css_parser_error_syntax (self: parser, format: "Not more than %d shadows supported", MAX_SHADOWS);
500 goto fail;
501 }
502 if (gtk_css_shadow_value_parse_one (parser, box_shadow_mode, result: &shadows[n_shadows]))
503 n_shadows++;
504
505 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
506
507 return gtk_css_shadow_value_new (shadows, n_shadows, FALSE);
508
509fail:
510 for (i = 0; i < n_shadows; i++)
511 {
512 const ShadowValue *shadow = &shadows[i];
513
514 shadow_value_unref (shadow);
515 }
516 return NULL;
517}
518
519GtkCssValue *
520gtk_css_shadow_value_parse_filter (GtkCssParser *parser)
521{
522 ShadowValue shadow;
523
524 if (gtk_css_shadow_value_parse_one (parser, FALSE, result: &shadow))
525 return gtk_css_shadow_value_new (shadows: &shadow, n_shadows: 1, TRUE);
526 else
527 return NULL;
528}
529
530void
531gtk_css_shadow_value_get_extents (const GtkCssValue *value,
532 GtkBorder *border)
533{
534 guint i;
535
536 memset (s: border, c: 0, n: sizeof (GtkBorder));
537
538 for (i = 0; i < value->n_shadows; i ++)
539 {
540 const ShadowValue *shadow = &value->shadows[i];
541 double hoffset, voffset, spread, radius, clip_radius;
542
543 spread = _gtk_css_number_value_get (number: shadow->spread, one_hundred_percent: 0);
544 radius = _gtk_css_number_value_get (number: shadow->radius, one_hundred_percent: 0);
545 if (!value->is_filter)
546 radius = radius / 2.0;
547 clip_radius = gsk_cairo_blur_compute_pixels (radius);
548 hoffset = _gtk_css_number_value_get (number: shadow->hoffset, one_hundred_percent: 0);
549 voffset = _gtk_css_number_value_get (number: shadow->voffset, one_hundred_percent: 0);
550
551 border->top = MAX (border->top, ceil (clip_radius + spread - voffset));
552 border->right = MAX (border->right, ceil (clip_radius + spread + hoffset));
553 border->bottom = MAX (border->bottom, ceil (clip_radius + spread + voffset));
554 border->left = MAX (border->left, ceil (clip_radius + spread - hoffset));
555 }
556}
557
558void
559gtk_css_shadow_value_snapshot_outset (const GtkCssValue *value,
560 GtkSnapshot *snapshot,
561 const GskRoundedRect *border_box)
562{
563 guint i;
564 double dx, dy, spread, radius;
565 const GdkRGBA *color;
566
567 g_return_if_fail (value->class == &GTK_CSS_VALUE_SHADOW);
568
569 for (i = 0; i < value->n_shadows; i ++)
570 {
571 const ShadowValue *shadow = &value->shadows[i];
572
573 if (shadow->inset)
574 continue;
575
576 color = gtk_css_color_value_get_rgba (color: shadow->color);
577
578 /* We don't need to draw invisible shadows */
579 if (gdk_rgba_is_clear (rgba: color))
580 continue;
581
582 dx = _gtk_css_number_value_get (number: shadow->hoffset, one_hundred_percent: 0);
583 dy = _gtk_css_number_value_get (number: shadow->voffset, one_hundred_percent: 0);
584 spread = _gtk_css_number_value_get (number: shadow->spread, one_hundred_percent: 0);
585 radius = _gtk_css_number_value_get (number: shadow->radius, one_hundred_percent: 0);
586 if (value->is_filter)
587 radius = 2 * radius;
588
589 gtk_snapshot_append_outset_shadow (snapshot, outline: border_box, color, dx, dy, spread, blur_radius: radius);
590 }
591}
592
593void
594gtk_css_shadow_value_snapshot_inset (const GtkCssValue *value,
595 GtkSnapshot *snapshot,
596 const GskRoundedRect *padding_box)
597{
598 guint i;
599 double dx, dy, spread, radius;
600 const GdkRGBA *color;
601
602 g_return_if_fail (value->class == &GTK_CSS_VALUE_SHADOW);
603
604 for (i = 0; i < value->n_shadows; i ++)
605 {
606 const ShadowValue *shadow = &value->shadows[i];
607
608 if (!shadow->inset)
609 continue;
610
611 color = gtk_css_color_value_get_rgba (color: shadow->color);
612
613 /* We don't need to draw invisible shadows */
614 if (gdk_rgba_is_clear (rgba: color))
615 continue;
616
617 dx = _gtk_css_number_value_get (number: shadow->hoffset, one_hundred_percent: 0);
618 dy = _gtk_css_number_value_get (number: shadow->voffset, one_hundred_percent: 0);
619 spread = _gtk_css_number_value_get (number: shadow->spread, one_hundred_percent: 0);
620 radius = _gtk_css_number_value_get (number: shadow->radius, one_hundred_percent: 0);
621 if (value->is_filter)
622 radius = 2 * radius;
623
624 /* These are trivial to do with a color node */
625 if (spread == 0 && radius == 0 &&
626 gsk_rounded_rect_is_rectilinear (self: padding_box))
627 {
628 const graphene_rect_t *padding_bounds = &padding_box->bounds;
629
630 if (dx > 0)
631 {
632 const float y = dy > 0 ? dy : 0;
633
634 gtk_snapshot_append_color (snapshot, color,
635 bounds: &GRAPHENE_RECT_INIT (
636 padding_bounds->origin.x,
637 padding_bounds->origin.y + y,
638 dx,
639 padding_bounds->size.height - ABS (dy)
640 ));
641 }
642 else if (dx < 0)
643 {
644 const float y = dy > 0 ? dy : 0;
645
646 gtk_snapshot_append_color (snapshot, color,
647 bounds: &GRAPHENE_RECT_INIT (
648 padding_bounds->origin.x + padding_bounds->size.width + dx,
649 padding_bounds->origin.y + y,
650 - dx,
651 padding_bounds->size.height - ABS (dy)
652 ));
653 }
654
655 if (dy > 0)
656 {
657 gtk_snapshot_append_color (snapshot, color,
658 bounds: &GRAPHENE_RECT_INIT (
659 padding_bounds->origin.x,
660 padding_bounds->origin.y,
661 padding_bounds->size.width,
662 dy
663 ));
664 }
665 else if (dy < 0)
666 {
667 gtk_snapshot_append_color (snapshot, color,
668 bounds: &GRAPHENE_RECT_INIT (
669 padding_bounds->origin.x,
670 padding_bounds->origin.y + padding_bounds->size.height + dy,
671 padding_bounds->size.width,
672 - dy
673 ));
674 }
675 }
676 else
677 {
678 gtk_snapshot_append_inset_shadow (snapshot,
679 outline: padding_box,
680 color,
681 dx, dy, spread, blur_radius: radius);
682 }
683 }
684}
685
686gboolean
687gtk_css_shadow_value_is_clear (const GtkCssValue *value)
688{
689 guint i;
690
691 if (!value)
692 return TRUE;
693
694 for (i = 0; i < value->n_shadows; i++)
695 {
696 const ShadowValue *shadow = &value->shadows[i];
697
698 if (!gdk_rgba_is_clear (rgba: gtk_css_color_value_get_rgba (color: shadow->color)))
699 return FALSE;
700 }
701
702 return TRUE;
703}
704
705gboolean
706gtk_css_shadow_value_is_none (const GtkCssValue *shadow)
707{
708 return !shadow || shadow->n_shadows == 0;
709}
710
711gboolean
712gtk_css_shadow_value_push_snapshot (const GtkCssValue *value,
713 GtkSnapshot *snapshot)
714{
715 gboolean need_shadow = FALSE;
716 guint i;
717
718 for (i = 0; i < value->n_shadows; i++)
719 {
720 const ShadowValue *shadow = &value->shadows[i];
721
722 if (!gdk_rgba_is_clear (rgba: gtk_css_color_value_get_rgba (color: shadow->color)))
723 {
724 need_shadow = TRUE;
725 break;
726 }
727 }
728
729 if (need_shadow)
730 {
731 GskShadow *shadows = g_newa (GskShadow, value->n_shadows);
732
733 for (i = 0; i < value->n_shadows; i++)
734 {
735 const ShadowValue *shadow = &value->shadows[i];
736
737 shadows[i].dx = _gtk_css_number_value_get (number: shadow->hoffset, one_hundred_percent: 0);
738 shadows[i].dy = _gtk_css_number_value_get (number: shadow->voffset, one_hundred_percent: 0);
739 shadows[i].color = *gtk_css_color_value_get_rgba (color: shadow->color);
740 shadows[i].radius = _gtk_css_number_value_get (number: shadow->radius, one_hundred_percent: 0);
741 if (value->is_filter)
742 shadows[i].radius *= 2;
743 }
744
745 gtk_snapshot_push_shadow (snapshot, shadow: shadows, n_shadows: value->n_shadows);
746 }
747
748 return need_shadow;
749}
750
751void
752gtk_css_shadow_value_pop_snapshot (const GtkCssValue *value,
753 GtkSnapshot *snapshot)
754{
755 gboolean need_shadow = FALSE;
756 guint i;
757
758 for (i = 0; i < value->n_shadows; i++)
759 {
760 const ShadowValue *shadow = &value->shadows[i];
761
762 if (!gdk_rgba_is_clear (rgba: gtk_css_color_value_get_rgba (color: shadow->color)))
763 {
764 need_shadow = TRUE;
765 break;
766 }
767 }
768
769 if (need_shadow)
770 gtk_snapshot_pop (snapshot);
771}
772

source code of gtk/gtk/gtkcssshadowvalue.c