1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2016 Benjamin Otte <otte@gnome.org>
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 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
18 #include "config.h"
19
20 #include "gtksnapshot.h"
21 #include "gtksnapshotprivate.h"
22
23 #include "gtkcsscolorvalueprivate.h"
24 #include "gtkcssshadowvalueprivate.h"
25 #include "gtkdebug.h"
26 #include "gtkrenderbackgroundprivate.h"
27 #include "gtkrenderborderprivate.h"
28 #include "gtkrendericonprivate.h"
29 #include "gtkrendernodepaintableprivate.h"
30 #include "gtkstylecontextprivate.h"
31 #include "gsktransformprivate.h"
32
33 #include "gdk/gdkrgbaprivate.h"
34
35 #include "gsk/gskrendernodeprivate.h"
36 #include "gsk/gskroundedrectprivate.h"
37
38 #include "gtk/gskpango.h"
39
40 #define GDK_ARRAY_NAME gtk_snapshot_nodes
41 #define GDK_ARRAY_TYPE_NAME GtkSnapshotNodes
42 #define GDK_ARRAY_ELEMENT_TYPE GskRenderNode *
43 #define GDK_ARRAY_FREE_FUNC gsk_render_node_unref
44 #include "gdk/gdkarrayimpl.c"
45
46 /**
47 * GtkSnapshot:
48 *
49 * `GtkSnapshot` assists in creating `GskRenderNodes` for widgets.
50 *
51 * It functions in a similar way to a cairo context, and maintains a stack
52 * of render nodes and their associated transformations.
53 *
54 * The node at the top of the stack is the the one that gtk_snapshot_append_…
55 * functions operate on. Use the gtk_snapshot_push_… functions and
56 * gtk_snapshot_pop() to change the current node.
57 *
58 * The typical way to obtain a `GtkSnapshot` object is as an argument to
59 * the [vfunc@Gtk.Widget.snapshot] vfunc. If you need to create your own
60 * `GtkSnapshot`, use [ctor@Gtk.Snapshot.new].
61 */
62
63 typedef struct _GtkSnapshotState GtkSnapshotState;
64
65 typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GtkSnapshot *snapshot,
66 GtkSnapshotState *state,
67 GskRenderNode **nodes,
68 guint n_nodes);
69 typedef void (* GtkSnapshotClearFunc) (GtkSnapshotState *state);
70
71 struct _GtkSnapshotState {
72 guint start_node_index;
73 guint n_nodes;
74
75 GskTransform * transform;
76
77 GtkSnapshotCollectFunc collect_func;
78 GtkSnapshotClearFunc clear_func;
79 union {
80 struct {
81 double opacity;
82 } opacity;
83 struct {
84 double radius;
85 } blur;
86 struct {
87 graphene_matrix_t matrix;
88 graphene_vec4_t offset;
89 } color_matrix;
90 struct {
91 graphene_rect_t bounds;
92 graphene_rect_t child_bounds;
93 } repeat;
94 struct {
95 graphene_rect_t bounds;
96 } clip;
97 struct {
98 GskGLShader *shader;
99 GBytes *args;
100 graphene_rect_t bounds;
101 GskRenderNode **nodes;
102 GskRenderNode *internal_nodes[4];
103 } glshader;
104 struct {
105 graphene_rect_t bounds;
106 int node_idx;
107 int n_children;
108 } glshader_texture;
109 struct {
110 GskRoundedRect bounds;
111 } rounded_clip;
112 struct {
113 gsize n_shadows;
114 GskShadow *shadows;
115 GskShadow a_shadow; /* Used if n_shadows == 1 */
116 } shadow;
117 struct {
118 GskBlendMode blend_mode;
119 GskRenderNode *bottom_node;
120 } blend;
121 struct {
122 double progress;
123 GskRenderNode *start_node;
124 } cross_fade;
125 struct {
126 char *message;
127 } debug;
128 } data;
129 };
130
131 static void gtk_snapshot_state_clear (GtkSnapshotState *state);
132
133 #define GDK_ARRAY_NAME gtk_snapshot_states
134 #define GDK_ARRAY_TYPE_NAME GtkSnapshotStates
135 #define GDK_ARRAY_ELEMENT_TYPE GtkSnapshotState
136 #define GDK_ARRAY_FREE_FUNC gtk_snapshot_state_clear
137 #define GDK_ARRAY_BY_VALUE 1
138 #define GDK_ARRAY_PREALLOC 16
139 #define GDK_ARRAY_NO_MEMSET 1
140 #include "gdk/gdkarrayimpl.c"
141
142 /* This is a nasty little hack. We typedef GtkSnapshot to the fake object GdkSnapshot
143 * so that we don't need to typecast between them.
144 * After all, the GdkSnapshot only exist so poor language bindings don't trip. Hardcore
145 * C code can just blatantly ignore such layering violations with a typedef.
146 */
147 struct _GdkSnapshot {
148 GObject parent_instance; /* it's really GdkSnapshot, but don't tell anyone! */
149
150 GtkSnapshotStates state_stack;
151 GtkSnapshotNodes nodes;
152 };
153
154 struct _GtkSnapshotClass {
155 GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */
156 };
157
G_DEFINE_TYPE(GtkSnapshot,gtk_snapshot,GDK_TYPE_SNAPSHOT)158 G_DEFINE_TYPE (GtkSnapshot, gtk_snapshot, GDK_TYPE_SNAPSHOT)
159
160 static void
161 gtk_snapshot_dispose (GObject *object)
162 {
163 GtkSnapshot *snapshot = GTK_SNAPSHOT (object);
164
165 if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
166 gsk_render_node_unref (gtk_snapshot_to_node (snapshot));
167
168 g_assert (gtk_snapshot_states_is_empty (&snapshot->state_stack));
169 g_assert (gtk_snapshot_nodes_is_empty (&snapshot->nodes));
170
171 G_OBJECT_CLASS (gtk_snapshot_parent_class)->dispose (object);
172 }
173
174 static void
gtk_snapshot_class_init(GtkSnapshotClass * klass)175 gtk_snapshot_class_init (GtkSnapshotClass *klass)
176 {
177 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
178
179 gobject_class->dispose = gtk_snapshot_dispose;
180 }
181
182 static void
gtk_snapshot_init(GtkSnapshot * self)183 gtk_snapshot_init (GtkSnapshot *self)
184 {
185 }
186
187 static GskRenderNode *
gtk_snapshot_collect_default(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)188 gtk_snapshot_collect_default (GtkSnapshot *snapshot,
189 GtkSnapshotState *state,
190 GskRenderNode **nodes,
191 guint n_nodes)
192 {
193 GskRenderNode *node;
194
195 if (n_nodes == 0)
196 {
197 node = NULL;
198 }
199 else if (n_nodes == 1)
200 {
201 node = gsk_render_node_ref (nodes[0]);
202 }
203 else
204 {
205 node = gsk_container_node_new (nodes, n_nodes);
206 }
207
208 return node;
209 }
210
211 static GtkSnapshotState *
gtk_snapshot_push_state(GtkSnapshot * snapshot,GskTransform * transform,GtkSnapshotCollectFunc collect_func,GtkSnapshotClearFunc clear_func)212 gtk_snapshot_push_state (GtkSnapshot *snapshot,
213 GskTransform *transform,
214 GtkSnapshotCollectFunc collect_func,
215 GtkSnapshotClearFunc clear_func)
216 {
217 const gsize n_states = gtk_snapshot_states_get_size (&snapshot->state_stack);
218 GtkSnapshotState *state;
219
220 gtk_snapshot_states_set_size (&snapshot->state_stack, n_states + 1);
221 state = gtk_snapshot_states_get (&snapshot->state_stack, n_states);
222
223 state->transform = gsk_transform_ref (transform);
224 state->collect_func = collect_func;
225 state->clear_func = clear_func;
226 state->start_node_index = gtk_snapshot_nodes_get_size (&snapshot->nodes);
227 state->n_nodes = 0;
228
229 return state;
230 }
231
232 static GtkSnapshotState *
gtk_snapshot_get_current_state(const GtkSnapshot * snapshot)233 gtk_snapshot_get_current_state (const GtkSnapshot *snapshot)
234 {
235 gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
236
237 g_assert (size > 0);
238
239 return gtk_snapshot_states_get (&snapshot->state_stack, size - 1);
240 }
241
242 static GtkSnapshotState *
gtk_snapshot_get_previous_state(const GtkSnapshot * snapshot)243 gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot)
244 {
245 gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
246
247 g_assert (size > 1);
248
249 return gtk_snapshot_states_get (&snapshot->state_stack, size - 2);
250 }
251
252 /* n == 0 => current, n == 1, previous, etc */
253 static GtkSnapshotState *
gtk_snapshot_get_nth_previous_state(const GtkSnapshot * snapshot,int n)254 gtk_snapshot_get_nth_previous_state (const GtkSnapshot *snapshot,
255 int n)
256 {
257 gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
258
259 g_assert (size > n);
260
261 return gtk_snapshot_states_get (&snapshot->state_stack, size - (1 + n));
262 }
263
264 static void
gtk_snapshot_state_clear(GtkSnapshotState * state)265 gtk_snapshot_state_clear (GtkSnapshotState *state)
266 {
267 if (state->clear_func)
268 state->clear_func (state);
269
270 gsk_transform_unref (state->transform);
271 }
272
273 /**
274 * gtk_snapshot_new:
275 *
276 * Creates a new `GtkSnapshot`.
277 *
278 * Returns: a newly-allocated `GtkSnapshot`
279 */
280 GtkSnapshot *
gtk_snapshot_new(void)281 gtk_snapshot_new (void)
282 {
283 GtkSnapshot *snapshot;
284
285 snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
286
287 gtk_snapshot_states_init (&snapshot->state_stack);
288 gtk_snapshot_nodes_init (&snapshot->nodes);
289
290 gtk_snapshot_push_state (snapshot,
291 NULL,
292 gtk_snapshot_collect_default,
293 NULL);
294
295 return snapshot;
296 }
297
298 /**
299 * gtk_snapshot_free_to_node: (skip)
300 * @snapshot: (transfer full): a `GtkSnapshot`
301 *
302 * Returns the node that was constructed by @snapshot
303 * and frees @snapshot.
304 *
305 * Returns: (transfer full): a newly-created `GskRenderNode`
306 */
307 GskRenderNode *
gtk_snapshot_free_to_node(GtkSnapshot * snapshot)308 gtk_snapshot_free_to_node (GtkSnapshot *snapshot)
309 {
310 GskRenderNode *result;
311
312 result = gtk_snapshot_to_node (snapshot);
313 g_object_unref (snapshot);
314
315 return result;
316 }
317
318 /**
319 * gtk_snapshot_free_to_paintable: (skip)
320 * @snapshot: (transfer full): a `GtkSnapshot`
321 * @size: (nullable): The size of the resulting paintable
322 * or %NULL to use the bounds of the snapshot
323 *
324 * Returns a paintable for the node that was
325 * constructed by @snapshot and frees @snapshot.
326 *
327 * Returns: (transfer full): a newly-created `GdkPaintable`
328 */
329 GdkPaintable *
gtk_snapshot_free_to_paintable(GtkSnapshot * snapshot,const graphene_size_t * size)330 gtk_snapshot_free_to_paintable (GtkSnapshot *snapshot,
331 const graphene_size_t *size)
332 {
333 GdkPaintable *result;
334
335 result = gtk_snapshot_to_paintable (snapshot, size);
336 g_object_unref (snapshot);
337
338 return result;
339 }
340
341 static GskRenderNode *
gtk_snapshot_collect_autopush_transform(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)342 gtk_snapshot_collect_autopush_transform (GtkSnapshot *snapshot,
343 GtkSnapshotState *state,
344 GskRenderNode **nodes,
345 guint n_nodes)
346 {
347 GskRenderNode *node, *transform_node;
348 GtkSnapshotState *previous_state;
349
350 previous_state = gtk_snapshot_get_previous_state (snapshot);
351
352 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
353 if (node == NULL)
354 return NULL;
355
356 transform_node = gsk_transform_node_new (node, previous_state->transform);
357
358 gsk_render_node_unref (node);
359
360 return transform_node;
361 }
362
363 static void
gtk_snapshot_autopush_transform(GtkSnapshot * snapshot)364 gtk_snapshot_autopush_transform (GtkSnapshot *snapshot)
365 {
366 gtk_snapshot_push_state (snapshot,
367 NULL,
368 gtk_snapshot_collect_autopush_transform,
369 NULL);
370 }
371
372 static gboolean
gtk_snapshot_state_should_autopop(const GtkSnapshotState * state)373 gtk_snapshot_state_should_autopop (const GtkSnapshotState *state)
374 {
375 return state->collect_func == gtk_snapshot_collect_autopush_transform;
376 }
377
378 static GskRenderNode *
gtk_snapshot_collect_debug(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)379 gtk_snapshot_collect_debug (GtkSnapshot *snapshot,
380 GtkSnapshotState *state,
381 GskRenderNode **nodes,
382 guint n_nodes)
383 {
384 GskRenderNode *node, *debug_node;
385
386 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
387 if (node == NULL)
388 return NULL;
389
390 debug_node = gsk_debug_node_new (node, state->data.debug.message);
391 state->data.debug.message = NULL;
392
393 gsk_render_node_unref (node);
394
395 return debug_node;
396 }
397
398 static void
gtk_snapshot_clear_debug(GtkSnapshotState * state)399 gtk_snapshot_clear_debug (GtkSnapshotState *state)
400 {
401 g_clear_pointer (&state->data.debug.message, g_free);
402 }
403
404 /**
405 * gtk_snapshot_push_debug:
406 * @snapshot: a `GtkSnapshot`
407 * @message: a printf-style format string
408 * @...: arguments for @message
409 *
410 * Inserts a debug node with a message.
411 *
412 * Debug nodes don't affect the rendering at all, but can be
413 * helpful in identifying parts of a render node tree dump,
414 * for example in the GTK inspector.
415 */
416 void
gtk_snapshot_push_debug(GtkSnapshot * snapshot,const char * message,...)417 gtk_snapshot_push_debug (GtkSnapshot *snapshot,
418 const char *message,
419 ...)
420 {
421 GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
422
423 if (GTK_DEBUG_CHECK (SNAPSHOT))
424 {
425 va_list args;
426 GtkSnapshotState *state;
427
428 state = gtk_snapshot_push_state (snapshot,
429 current_state->transform,
430 gtk_snapshot_collect_debug,
431 gtk_snapshot_clear_debug);
432
433
434
435 va_start (args, message);
436 state->data.debug.message = g_strdup_vprintf (message, args);
437 va_end (args);
438 }
439 else
440 {
441 gtk_snapshot_push_state (snapshot,
442 current_state->transform,
443 gtk_snapshot_collect_default,
444 NULL);
445 }
446 }
447
448 static GskRenderNode *
gtk_snapshot_collect_opacity(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)449 gtk_snapshot_collect_opacity (GtkSnapshot *snapshot,
450 GtkSnapshotState *state,
451 GskRenderNode **nodes,
452 guint n_nodes)
453 {
454 GskRenderNode *node, *opacity_node;
455
456 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
457 if (node == NULL)
458 return NULL;
459
460 if (state->data.opacity.opacity == 1.0)
461 {
462 opacity_node = node;
463 }
464 else if (state->data.opacity.opacity == 0.0)
465 {
466 GdkRGBA color = GDK_RGBA ("00000000");
467 graphene_rect_t bounds;
468
469 gsk_render_node_get_bounds (node, &bounds);
470 opacity_node = gsk_color_node_new (&color, &bounds);
471 gsk_render_node_unref (node);
472 }
473 else
474 {
475 opacity_node = gsk_opacity_node_new (node, state->data.opacity.opacity);
476 gsk_render_node_unref (node);
477 }
478
479 return opacity_node;
480 }
481
482 /**
483 * gtk_snapshot_push_opacity:
484 * @snapshot: a `GtkSnapshot`
485 * @opacity: the opacity to use
486 *
487 * Modifies the opacity of an image.
488 *
489 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
490 */
491 void
gtk_snapshot_push_opacity(GtkSnapshot * snapshot,double opacity)492 gtk_snapshot_push_opacity (GtkSnapshot *snapshot,
493 double opacity)
494 {
495 GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
496 GtkSnapshotState *state;
497
498 state = gtk_snapshot_push_state (snapshot,
499 current_state->transform,
500 gtk_snapshot_collect_opacity,
501 NULL);
502 state->data.opacity.opacity = CLAMP (opacity, 0.0, 1.0);
503 }
504
505 static GskRenderNode *
gtk_snapshot_collect_blur(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)506 gtk_snapshot_collect_blur (GtkSnapshot *snapshot,
507 GtkSnapshotState *state,
508 GskRenderNode **nodes,
509 guint n_nodes)
510 {
511 GskRenderNode *node, *blur_node;
512 double radius;
513
514 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
515 if (node == NULL)
516 return NULL;
517
518 radius = state->data.blur.radius;
519
520 if (radius == 0.0)
521 return node;
522
523 blur_node = gsk_blur_node_new (node, radius);
524
525 gsk_render_node_unref (node);
526
527 return blur_node;
528 }
529
530 /**
531 * gtk_snapshot_push_blur:
532 * @snapshot: a `GtkSnapshot`
533 * @radius: the blur radius to use
534 *
535 * Blurs an image.
536 *
537 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
538 */
539 void
gtk_snapshot_push_blur(GtkSnapshot * snapshot,double radius)540 gtk_snapshot_push_blur (GtkSnapshot *snapshot,
541 double radius)
542 {
543 const GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
544 GtkSnapshotState *state;
545
546 state = gtk_snapshot_push_state (snapshot,
547 current_state->transform,
548 gtk_snapshot_collect_blur,
549 NULL);
550 state->data.blur.radius = radius;
551 }
552
553 static GskRenderNode *
merge_color_matrix_nodes(const graphene_matrix_t * matrix2,const graphene_vec4_t * offset2,GskRenderNode * child)554 merge_color_matrix_nodes (const graphene_matrix_t *matrix2,
555 const graphene_vec4_t *offset2,
556 GskRenderNode *child)
557 {
558 const graphene_matrix_t *mat1 = gsk_color_matrix_node_get_color_matrix (child);
559 const graphene_vec4_t *offset1 = gsk_color_matrix_node_get_color_offset (child);
560 graphene_matrix_t mat2 = *matrix2;
561 graphene_vec4_t off2 = *offset2;
562 GskRenderNode *result;
563
564 g_assert (gsk_render_node_get_node_type (child) == GSK_COLOR_MATRIX_NODE);
565
566 /* color matrix node: color = mat * p + offset; for a pixel p.
567 * color = mat1 * (mat2 * p + offset2) + offset1;
568 * = mat1 * mat2 * p + offset2 * mat1 + offset1
569 * = (mat1 * mat2) * p + (offset2 * mat1 + offset1)
570 * Which this code does.
571 * mat1 and offset1 come from @child.
572 */
573
574 graphene_matrix_transform_vec4 (mat1, offset2, &off2);
575 graphene_vec4_add (&off2, offset1, &off2);
576
577 graphene_matrix_multiply (mat1, &mat2, &mat2);
578
579 result = gsk_color_matrix_node_new (gsk_color_matrix_node_get_child (child),
580 &mat2, &off2);
581
582 return result;
583 }
584
585 static GskRenderNode *
gtk_snapshot_collect_color_matrix(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)586 gtk_snapshot_collect_color_matrix (GtkSnapshot *snapshot,
587 GtkSnapshotState *state,
588 GskRenderNode **nodes,
589 guint n_nodes)
590 {
591 GskRenderNode *node, *result;
592
593 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
594 if (node == NULL)
595 return NULL;
596
597 if (gsk_render_node_get_node_type (node) == GSK_COLOR_MATRIX_NODE)
598 {
599 result = merge_color_matrix_nodes (&state->data.color_matrix.matrix,
600 &state->data.color_matrix.offset,
601 node);
602 gsk_render_node_unref (node);
603 }
604 else if (gsk_render_node_get_node_type (node) == GSK_TRANSFORM_NODE)
605 {
606 GskRenderNode *transform_child = gsk_transform_node_get_child (node);
607 GskRenderNode *color_matrix;
608
609 if (gsk_render_node_get_node_type (transform_child) == GSK_COLOR_MATRIX_NODE)
610 {
611 color_matrix = merge_color_matrix_nodes (&state->data.color_matrix.matrix,
612 &state->data.color_matrix.offset,
613 transform_child);
614 }
615 else
616 {
617 color_matrix = gsk_color_matrix_node_new (transform_child,
618 &state->data.color_matrix.matrix,
619 &state->data.color_matrix.offset);
620 }
621 result = gsk_transform_node_new (color_matrix,
622 gsk_transform_node_get_transform (node));
623 gsk_render_node_unref (color_matrix);
624 gsk_render_node_unref (node);
625 node = NULL;
626 }
627 else
628 {
629 result = gsk_color_matrix_node_new (node,
630 &state->data.color_matrix.matrix,
631 &state->data.color_matrix.offset);
632 gsk_render_node_unref (node);
633 }
634
635 return result;
636 }
637
638 /**
639 * gtk_snapshot_push_color_matrix:
640 * @snapshot: a `GtkSnapshot`
641 * @color_matrix: the color matrix to use
642 * @color_offset: the color offset to use
643 *
644 * Modifies the colors of an image by applying an affine transformation
645 * in RGB space.
646 *
647 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
648 */
649 void
gtk_snapshot_push_color_matrix(GtkSnapshot * snapshot,const graphene_matrix_t * color_matrix,const graphene_vec4_t * color_offset)650 gtk_snapshot_push_color_matrix (GtkSnapshot *snapshot,
651 const graphene_matrix_t *color_matrix,
652 const graphene_vec4_t *color_offset)
653 {
654 const GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
655 GtkSnapshotState *state;
656
657 state = gtk_snapshot_push_state (snapshot,
658 current_state->transform,
659 gtk_snapshot_collect_color_matrix,
660 NULL);
661
662 graphene_matrix_init_from_matrix (&state->data.color_matrix.matrix, color_matrix);
663 graphene_vec4_init_from_vec4 (&state->data.color_matrix.offset, color_offset);
664 }
665
666 static GskRenderNode *
gtk_snapshot_collect_repeat(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)667 gtk_snapshot_collect_repeat (GtkSnapshot *snapshot,
668 GtkSnapshotState *state,
669 GskRenderNode **nodes,
670 guint n_nodes)
671 {
672 GskRenderNode *node, *repeat_node;
673 const graphene_rect_t *bounds = &state->data.repeat.bounds;
674 const graphene_rect_t *child_bounds = &state->data.repeat.child_bounds;
675
676 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
677 if (node == NULL)
678 return NULL;
679
680 if (gsk_render_node_get_node_type (node) == GSK_COLOR_NODE &&
681 graphene_rect_equal (child_bounds, &node->bounds))
682 {
683 /* Repeating a color node entirely is pretty easy by just increasing
684 * the size of the color node. */
685 GskRenderNode *color_node = gsk_color_node_new (gsk_color_node_get_color (node), bounds);
686
687 gsk_render_node_unref (node);
688
689 return color_node;
690 }
691
692 repeat_node = gsk_repeat_node_new (bounds,
693 node,
694 child_bounds->size.width > 0 ? child_bounds : NULL);
695
696 gsk_render_node_unref (node);
697
698 return repeat_node;
699 }
700
701 static void
gtk_graphene_rect_scale_affine(const graphene_rect_t * rect,float scale_x,float scale_y,float dx,float dy,graphene_rect_t * res)702 gtk_graphene_rect_scale_affine (const graphene_rect_t *rect,
703 float scale_x,
704 float scale_y,
705 float dx,
706 float dy,
707 graphene_rect_t *res)
708 {
709 res->origin.x = scale_x * rect->origin.x + dx;
710 res->origin.y = scale_y * rect->origin.y + dy;
711 res->size.width = scale_x * rect->size.width;
712 res->size.height = scale_y * rect->size.height;
713
714 if (scale_x < 0 || scale_y < 0)
715 graphene_rect_normalize (res);
716 }
717
718 static void
gtk_snapshot_ensure_affine(GtkSnapshot * snapshot,float * scale_x,float * scale_y,float * dx,float * dy)719 gtk_snapshot_ensure_affine (GtkSnapshot *snapshot,
720 float *scale_x,
721 float *scale_y,
722 float *dx,
723 float *dy)
724 {
725 const GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot);
726
727 if (gsk_transform_get_category (state->transform) < GSK_TRANSFORM_CATEGORY_2D_AFFINE)
728 {
729 gtk_snapshot_autopush_transform (snapshot);
730 state = gtk_snapshot_get_current_state (snapshot);
731 gsk_transform_to_affine (state->transform, scale_x, scale_y, dx, dy);
732 }
733 else if (gsk_transform_get_category (state->transform) == GSK_TRANSFORM_CATEGORY_2D_AFFINE)
734 {
735 gsk_transform_to_affine (state->transform, scale_x, scale_y, dx, dy);
736 if (*scale_x < 0.0 || *scale_y < 0.0)
737 {
738 gtk_snapshot_autopush_transform (snapshot);
739 state = gtk_snapshot_get_current_state (snapshot);
740 gsk_transform_to_affine (state->transform, scale_x, scale_y, dx, dy);
741 }
742 }
743 else
744 {
745 gsk_transform_to_affine (state->transform, scale_x, scale_y, dx, dy);
746 }
747 }
748
749 static void
gtk_snapshot_ensure_translate(GtkSnapshot * snapshot,float * dx,float * dy)750 gtk_snapshot_ensure_translate (GtkSnapshot *snapshot,
751 float *dx,
752 float *dy)
753 {
754 const GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot);
755
756 if (gsk_transform_get_category (state->transform) < GSK_TRANSFORM_CATEGORY_2D_TRANSLATE)
757 {
758 gtk_snapshot_autopush_transform (snapshot);
759 state = gtk_snapshot_get_current_state (snapshot);
760 }
761
762 gsk_transform_to_translate (state->transform, dx, dy);
763 }
764
765 static void
gtk_snapshot_ensure_identity(GtkSnapshot * snapshot)766 gtk_snapshot_ensure_identity (GtkSnapshot *snapshot)
767 {
768 const GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot);
769
770 if (gsk_transform_get_category (state->transform) < GSK_TRANSFORM_CATEGORY_IDENTITY)
771 gtk_snapshot_autopush_transform (snapshot);
772 }
773
774 /**
775 * gtk_snapshot_push_repeat:
776 * @snapshot: a `GtkSnapshot`
777 * @bounds: the bounds within which to repeat
778 * @child_bounds: (nullable): the bounds of the child or %NULL
779 * to use the full size of the collected child node
780 *
781 * Creates a node that repeats the child node.
782 *
783 * The child is recorded until the next call to [method@Gtk.Snapshot.pop].
784 */
785 void
gtk_snapshot_push_repeat(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_rect_t * child_bounds)786 gtk_snapshot_push_repeat (GtkSnapshot *snapshot,
787 const graphene_rect_t *bounds,
788 const graphene_rect_t *child_bounds)
789 {
790 GtkSnapshotState *state;
791 graphene_rect_t real_child_bounds = { { 0 } };
792 float scale_x, scale_y, dx, dy;
793
794 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
795
796 if (child_bounds)
797 gtk_graphene_rect_scale_affine (child_bounds, scale_x, scale_y, dx, dy, &real_child_bounds);
798
799 state = gtk_snapshot_push_state (snapshot,
800 gtk_snapshot_get_current_state (snapshot)->transform,
801 gtk_snapshot_collect_repeat,
802 NULL);
803
804 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.repeat.bounds);
805 state->data.repeat.child_bounds = real_child_bounds;
806 }
807
808 static GskRenderNode *
gtk_snapshot_collect_clip(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)809 gtk_snapshot_collect_clip (GtkSnapshot *snapshot,
810 GtkSnapshotState *state,
811 GskRenderNode **nodes,
812 guint n_nodes)
813 {
814 GskRenderNode *node, *clip_node;
815
816 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
817 if (node == NULL)
818 return NULL;
819
820 /* Check if the child node will even be clipped */
821 if (graphene_rect_contains_rect (&state->data.clip.bounds, &node->bounds))
822 return node;
823
824 if (state->data.clip.bounds.size.width == 0 ||
825 state->data.clip.bounds.size.height == 0)
826 return NULL;
827
828 clip_node = gsk_clip_node_new (node, &state->data.clip.bounds);
829
830 gsk_render_node_unref (node);
831
832 return clip_node;
833 }
834
835 /**
836 * gtk_snapshot_push_clip:
837 * @snapshot: a `GtkSnapshot`
838 * @bounds: the rectangle to clip to
839 *
840 * Clips an image to a rectangle.
841 *
842 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
843 */
844 void
gtk_snapshot_push_clip(GtkSnapshot * snapshot,const graphene_rect_t * bounds)845 gtk_snapshot_push_clip (GtkSnapshot *snapshot,
846 const graphene_rect_t *bounds)
847 {
848 GtkSnapshotState *state;
849 float scale_x, scale_y, dx, dy;
850
851 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
852
853 state = gtk_snapshot_push_state (snapshot,
854 gtk_snapshot_get_current_state (snapshot)->transform,
855 gtk_snapshot_collect_clip,
856 NULL);
857
858 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.clip.bounds);
859 }
860
861 static GskRenderNode *
gtk_snapshot_collect_gl_shader(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** collected_nodes,guint n_collected_nodes)862 gtk_snapshot_collect_gl_shader (GtkSnapshot *snapshot,
863 GtkSnapshotState *state,
864 GskRenderNode **collected_nodes,
865 guint n_collected_nodes)
866 {
867 GskRenderNode *shader_node = NULL;
868 GskRenderNode **nodes;
869 int n_children;
870
871 n_children = gsk_gl_shader_get_n_textures (state->data.glshader.shader);
872 shader_node = NULL;
873
874 if (n_collected_nodes != 0)
875 g_warning ("Unexpected children when poping gl shader.");
876
877 if (state->data.glshader.nodes)
878 nodes = state->data.glshader.nodes;
879 else
880 nodes = &state->data.glshader.internal_nodes[0];
881
882 if (state->data.glshader.bounds.size.width != 0 &&
883 state->data.glshader.bounds.size.height != 0)
884 shader_node = gsk_gl_shader_node_new (state->data.glshader.shader,
885 &state->data.glshader.bounds,
886 state->data.glshader.args,
887 nodes, n_children);
888
889 return shader_node;
890 }
891
892 static void
gtk_snapshot_clear_gl_shader(GtkSnapshotState * state)893 gtk_snapshot_clear_gl_shader (GtkSnapshotState *state)
894 {
895 GskRenderNode **nodes;
896 guint i, n_children;
897
898 n_children = gsk_gl_shader_get_n_textures (state->data.glshader.shader);
899
900 if (state->data.glshader.nodes)
901 nodes = state->data.glshader.nodes;
902 else
903 nodes = &state->data.glshader.internal_nodes[0];
904
905 g_object_unref (state->data.glshader.shader);
906 g_bytes_unref (state->data.glshader.args);
907
908 for (i = 0; i < n_children; i++)
909 gsk_render_node_unref (nodes[i]);
910
911 g_free (state->data.glshader.nodes);
912
913 }
914
915 static GskRenderNode *
gtk_snapshot_collect_gl_shader_texture(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)916 gtk_snapshot_collect_gl_shader_texture (GtkSnapshot *snapshot,
917 GtkSnapshotState *state,
918 GskRenderNode **nodes,
919 guint n_nodes)
920 {
921 GskRenderNode *child_node;
922 GdkRGBA transparent = { 0, 0, 0, 0 };
923 int n_children, node_idx;
924 GtkSnapshotState *glshader_state;
925 GskRenderNode **out_nodes;
926
927 child_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
928
929 if (child_node == NULL)
930 child_node = gsk_color_node_new (&transparent, &state->data.glshader_texture.bounds);
931
932 n_children = state->data.glshader_texture.n_children;
933 node_idx = state->data.glshader_texture.node_idx;
934
935 glshader_state = gtk_snapshot_get_nth_previous_state (snapshot, n_children - node_idx);
936 g_assert (glshader_state->collect_func == gtk_snapshot_collect_gl_shader);
937
938 if (glshader_state->data.glshader.nodes)
939 out_nodes = glshader_state->data.glshader.nodes;
940 else
941 out_nodes = &glshader_state->data.glshader.internal_nodes[0];
942
943 out_nodes[node_idx] = child_node;
944
945 return NULL;
946 }
947
948 /**
949 * gtk_snapshot_push_gl_shader:
950 * @snapshot: a `GtkSnapshot`
951 * @shader: The code to run
952 * @bounds: the rectangle to render into
953 * @take_args: (transfer full): Data block with arguments for the shader.
954 *
955 * Push a `GskGLShaderNode`.
956 *
957 * The node uses the given [class@Gsk.GLShader] and uniform values
958 * Additionally this takes a list of @n_children other nodes
959 * which will be passed to the `GskGLShaderNode`.
960 *
961 * The @take_args argument is a block of data to use for uniform
962 * arguments, as per types and offsets defined by the @shader.
963 * Normally this is generated by [method@Gsk.GLShader.format_args]
964 * or [struct@Gsk.ShaderArgsBuilder].
965 *
966 * The snapshotter takes ownership of @take_args, so the caller should
967 * not free it after this.
968 *
969 * If the renderer doesn't support GL shaders, or if there is any
970 * problem when compiling the shader, then the node will draw pink.
971 * You should use [method@Gsk.GLShader.compile] to ensure the @shader
972 * will work for the renderer before using it.
973 *
974 * If the shader requires textures (see [method@Gsk.GLShader.get_n_textures]),
975 * then it is expected that you call [method@Gtk.Snapshot.gl_shader_pop_texture]
976 * the number of times that are required. Each of these calls will generate
977 * a node that is added as a child to the `GskGLShaderNode`, which in turn
978 * will render these offscreen and pass as a texture to the shader.
979 *
980 * Once all textures (if any) are pop:ed, you must call the regular
981 * [method@Gtk.Snapshot.pop].
982 *
983 * If you want to use pre-existing textures as input to the shader rather
984 * than rendering new ones, use [method@Gtk.Snapshot.append_texture] to
985 * push a texture node. These will be used directly rather than being
986 * re-rendered.
987 *
988 * For details on how to write shaders, see [class@Gsk.GLShader].
989 */
990 void
gtk_snapshot_push_gl_shader(GtkSnapshot * snapshot,GskGLShader * shader,const graphene_rect_t * bounds,GBytes * take_args)991 gtk_snapshot_push_gl_shader (GtkSnapshot *snapshot,
992 GskGLShader *shader,
993 const graphene_rect_t *bounds,
994 GBytes *take_args)
995 {
996 GtkSnapshotState *state;
997 float scale_x, scale_y, dx, dy;
998 graphene_rect_t transformed_bounds;
999 int n_children = gsk_gl_shader_get_n_textures (shader);
1000
1001 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
1002
1003 state = gtk_snapshot_push_state (snapshot,
1004 gtk_snapshot_get_current_state (snapshot)->transform,
1005 gtk_snapshot_collect_gl_shader,
1006 gtk_snapshot_clear_gl_shader);
1007 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &transformed_bounds);
1008 state->data.glshader.bounds = transformed_bounds;
1009 state->data.glshader.shader = g_object_ref (shader);
1010 state->data.glshader.args = take_args; /* Takes ownership */
1011 if (n_children <= G_N_ELEMENTS (state->data.glshader.internal_nodes))
1012 state->data.glshader.nodes = NULL;
1013 else
1014 state->data.glshader.nodes = g_new (GskRenderNode *, n_children);
1015
1016 for (int i = 0; i < n_children; i++)
1017 {
1018 state = gtk_snapshot_push_state (snapshot,
1019 gtk_snapshot_get_current_state (snapshot)->transform,
1020 gtk_snapshot_collect_gl_shader_texture,
1021 NULL);
1022 state->data.glshader_texture.bounds = transformed_bounds;
1023 state->data.glshader_texture.node_idx = n_children - 1 - i;/* We pop in reverse order */
1024 state->data.glshader_texture.n_children = n_children;
1025 }
1026 }
1027
1028 static GskRenderNode *
gtk_snapshot_collect_rounded_clip(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1029 gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot,
1030 GtkSnapshotState *state,
1031 GskRenderNode **nodes,
1032 guint n_nodes)
1033 {
1034 GskRenderNode *node, *clip_node;
1035
1036 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1037 if (node == NULL)
1038 return NULL;
1039
1040 /* If the given radius is 0 in all corners, we can just create a normal clip node */
1041 if (gsk_rounded_rect_is_rectilinear (&state->data.rounded_clip.bounds))
1042 {
1043 /* ... and do the same optimization */
1044 if (graphene_rect_contains_rect (&state->data.rounded_clip.bounds.bounds, &node->bounds))
1045 return node;
1046
1047 clip_node = gsk_clip_node_new (node, &state->data.rounded_clip.bounds.bounds);
1048 }
1049 else
1050 {
1051 if (gsk_rounded_rect_contains_rect (&state->data.rounded_clip.bounds, &node->bounds))
1052 return node;
1053
1054 clip_node = gsk_rounded_clip_node_new (node, &state->data.rounded_clip.bounds);
1055 }
1056
1057 if (clip_node->bounds.size.width == 0 ||
1058 clip_node->bounds.size.height == 0)
1059 {
1060 gsk_render_node_unref (node);
1061 gsk_render_node_unref (clip_node);
1062 return NULL;
1063 }
1064
1065 gsk_render_node_unref (node);
1066
1067 return clip_node;
1068 }
1069
1070 /**
1071 * gtk_snapshot_push_rounded_clip:
1072 * @snapshot: a `GtkSnapshot`
1073 * @bounds: the rounded rectangle to clip to
1074 *
1075 * Clips an image to a rounded rectangle.
1076 *
1077 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
1078 */
1079 void
gtk_snapshot_push_rounded_clip(GtkSnapshot * snapshot,const GskRoundedRect * bounds)1080 gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
1081 const GskRoundedRect *bounds)
1082 {
1083 GtkSnapshotState *state;
1084 float scale_x, scale_y, dx, dy;
1085
1086 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
1087
1088 state = gtk_snapshot_push_state (snapshot,
1089 gtk_snapshot_get_current_state (snapshot)->transform,
1090 gtk_snapshot_collect_rounded_clip,
1091 NULL);
1092
1093 gsk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
1094 }
1095
1096 static GskRenderNode *
gtk_snapshot_collect_shadow(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1097 gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
1098 GtkSnapshotState *state,
1099 GskRenderNode **nodes,
1100 guint n_nodes)
1101 {
1102 GskRenderNode *node, *shadow_node;
1103
1104 node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1105 if (node == NULL)
1106 return NULL;
1107
1108 shadow_node = gsk_shadow_node_new (node,
1109 state->data.shadow.shadows != NULL ?
1110 state->data.shadow.shadows :
1111 &state->data.shadow.a_shadow,
1112 state->data.shadow.n_shadows);
1113
1114 gsk_render_node_unref (node);
1115
1116 return shadow_node;
1117 }
1118
1119 static void
gtk_snapshot_clear_shadow(GtkSnapshotState * state)1120 gtk_snapshot_clear_shadow (GtkSnapshotState *state)
1121 {
1122 g_free (state->data.shadow.shadows);
1123 }
1124
1125 /**
1126 * gtk_snapshot_push_shadow:
1127 * @snapshot: a `GtkSnapshot`
1128 * @shadow: the first shadow specification
1129 * @n_shadows: number of shadow specifications
1130 *
1131 * Applies a shadow to an image.
1132 *
1133 * The image is recorded until the next call to [method@Gtk.Snapshot.pop].
1134 */
1135 void
gtk_snapshot_push_shadow(GtkSnapshot * snapshot,const GskShadow * shadow,gsize n_shadows)1136 gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
1137 const GskShadow *shadow,
1138 gsize n_shadows)
1139 {
1140 const GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
1141 GtkSnapshotState *state;
1142
1143 state = gtk_snapshot_push_state (snapshot,
1144 current_state->transform,
1145 gtk_snapshot_collect_shadow,
1146 gtk_snapshot_clear_shadow);
1147
1148 state->data.shadow.n_shadows = n_shadows;
1149 if (n_shadows == 1)
1150 {
1151 state->data.shadow.shadows = NULL;
1152 memcpy (&state->data.shadow.a_shadow, shadow, sizeof (GskShadow));
1153 }
1154 else
1155 {
1156 state->data.shadow.shadows = g_malloc (sizeof (GskShadow) * n_shadows);
1157 memcpy (state->data.shadow.shadows, shadow, sizeof (GskShadow) * n_shadows);
1158 }
1159
1160 }
1161
1162 static GskRenderNode *
gtk_snapshot_collect_blend_top(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1163 gtk_snapshot_collect_blend_top (GtkSnapshot *snapshot,
1164 GtkSnapshotState *state,
1165 GskRenderNode **nodes,
1166 guint n_nodes)
1167 {
1168 GskRenderNode *bottom_node, *top_node, *blend_node;
1169 GdkRGBA transparent = { 0, 0, 0, 0 };
1170
1171 top_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1172 bottom_node = state->data.blend.bottom_node != NULL
1173 ? gsk_render_node_ref (state->data.blend.bottom_node)
1174 : NULL;
1175
1176 g_assert (top_node != NULL || bottom_node != NULL);
1177
1178 /* XXX: Is this necessary? Do we need a NULL node? */
1179 if (top_node == NULL)
1180 top_node = gsk_color_node_new (&transparent, &bottom_node->bounds);
1181 if (bottom_node == NULL)
1182 bottom_node = gsk_color_node_new (&transparent, &top_node->bounds);
1183
1184 blend_node = gsk_blend_node_new (bottom_node, top_node, state->data.blend.blend_mode);
1185
1186 gsk_render_node_unref (top_node);
1187 gsk_render_node_unref (bottom_node);
1188
1189 return blend_node;
1190 }
1191
1192 static void
gtk_snapshot_clear_blend_top(GtkSnapshotState * state)1193 gtk_snapshot_clear_blend_top (GtkSnapshotState *state)
1194 {
1195 g_clear_pointer (&(state->data.blend.bottom_node), gsk_render_node_unref);
1196 }
1197
1198 static GskRenderNode *
gtk_snapshot_collect_blend_bottom(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1199 gtk_snapshot_collect_blend_bottom (GtkSnapshot *snapshot,
1200 GtkSnapshotState *state,
1201 GskRenderNode **nodes,
1202 guint n_nodes)
1203 {
1204 GtkSnapshotState *prev_state = gtk_snapshot_get_previous_state (snapshot);
1205
1206 g_assert (prev_state->collect_func == gtk_snapshot_collect_blend_top);
1207
1208 prev_state->data.blend.bottom_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1209
1210 return NULL;
1211 }
1212
1213 /**
1214 * gtk_snapshot_push_blend:
1215 * @snapshot: a `GtkSnapshot`
1216 * @blend_mode: blend mode to use
1217 *
1218 * Blends together two images with the given blend mode.
1219 *
1220 * Until the first call to [method@Gtk.Snapshot.pop], the
1221 * bottom image for the blend operation will be recorded.
1222 * After that call, the top image to be blended will be
1223 * recorded until the second call to [method@Gtk.Snapshot.pop].
1224 *
1225 * Calling this function requires two subsequent calls
1226 * to [method@Gtk.Snapshot.pop].
1227 */
1228 void
gtk_snapshot_push_blend(GtkSnapshot * snapshot,GskBlendMode blend_mode)1229 gtk_snapshot_push_blend (GtkSnapshot *snapshot,
1230 GskBlendMode blend_mode)
1231 {
1232 GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
1233 GtkSnapshotState *top_state;
1234
1235 top_state = gtk_snapshot_push_state (snapshot,
1236 current_state->transform,
1237 gtk_snapshot_collect_blend_top,
1238 gtk_snapshot_clear_blend_top);
1239 top_state->data.blend.blend_mode = blend_mode;
1240
1241 gtk_snapshot_push_state (snapshot,
1242 top_state->transform,
1243 gtk_snapshot_collect_blend_bottom,
1244 NULL);
1245 }
1246
1247 static GskRenderNode *
gtk_snapshot_collect_cross_fade_end(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1248 gtk_snapshot_collect_cross_fade_end (GtkSnapshot *snapshot,
1249 GtkSnapshotState *state,
1250 GskRenderNode **nodes,
1251 guint n_nodes)
1252 {
1253 GskRenderNode *start_node, *end_node, *node;
1254
1255 end_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1256 start_node = state->data.cross_fade.start_node;
1257 state->data.cross_fade.start_node = NULL;
1258
1259 if (state->data.cross_fade.progress <= 0.0)
1260 {
1261 node = start_node;
1262
1263 if (end_node)
1264 gsk_render_node_unref (end_node);
1265 }
1266 else if (state->data.cross_fade.progress >= 1.0)
1267 {
1268 node = end_node;
1269
1270 if (start_node)
1271 gsk_render_node_unref (start_node);
1272 }
1273 else if (start_node && end_node)
1274 {
1275 node = gsk_cross_fade_node_new (start_node, end_node, state->data.cross_fade.progress);
1276
1277 gsk_render_node_unref (start_node);
1278 gsk_render_node_unref (end_node);
1279 }
1280 else if (start_node)
1281 {
1282 node = gsk_opacity_node_new (start_node, 1.0 - state->data.cross_fade.progress);
1283
1284 gsk_render_node_unref (start_node);
1285 }
1286 else if (end_node)
1287 {
1288 node = gsk_opacity_node_new (end_node, state->data.cross_fade.progress);
1289
1290 gsk_render_node_unref (end_node);
1291 }
1292 else
1293 {
1294 node = NULL;
1295 }
1296
1297 return node;
1298 }
1299
1300 static void
gtk_snapshot_clear_cross_fade_end(GtkSnapshotState * state)1301 gtk_snapshot_clear_cross_fade_end (GtkSnapshotState *state)
1302 {
1303 g_clear_pointer (&state->data.cross_fade.start_node, gsk_render_node_unref);
1304 }
1305
1306 static GskRenderNode *
gtk_snapshot_collect_cross_fade_start(GtkSnapshot * snapshot,GtkSnapshotState * state,GskRenderNode ** nodes,guint n_nodes)1307 gtk_snapshot_collect_cross_fade_start (GtkSnapshot *snapshot,
1308 GtkSnapshotState *state,
1309 GskRenderNode **nodes,
1310 guint n_nodes)
1311 {
1312 GtkSnapshotState *prev_state = gtk_snapshot_get_previous_state (snapshot);
1313
1314 g_assert (prev_state->collect_func == gtk_snapshot_collect_cross_fade_end);
1315
1316 prev_state->data.cross_fade.start_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
1317
1318 return NULL;
1319 }
1320
1321 /**
1322 * gtk_snapshot_push_cross_fade:
1323 * @snapshot: a `GtkSnapshot`
1324 * @progress: progress between 0.0 and 1.0
1325 *
1326 * Snapshots a cross-fade operation between two images with the
1327 * given @progress.
1328 *
1329 * Until the first call to [method@Gtk.Snapshot.pop], the start image
1330 * will be snapshot. After that call, the end image will be recorded
1331 * until the second call to [method@Gtk.Snapshot.pop].
1332 *
1333 * Calling this function requires two subsequent calls
1334 * to [method@Gtk.Snapshot.pop].
1335 */
1336 void
gtk_snapshot_push_cross_fade(GtkSnapshot * snapshot,double progress)1337 gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot,
1338 double progress)
1339 {
1340 const GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
1341 GtkSnapshotState *end_state;
1342
1343 end_state = gtk_snapshot_push_state (snapshot,
1344 current_state->transform,
1345 gtk_snapshot_collect_cross_fade_end,
1346 gtk_snapshot_clear_cross_fade_end);
1347 end_state->data.cross_fade.progress = progress;
1348
1349 gtk_snapshot_push_state (snapshot,
1350 end_state->transform,
1351 gtk_snapshot_collect_cross_fade_start,
1352 NULL);
1353 }
1354
1355 static GskRenderNode *
gtk_snapshot_pop_one(GtkSnapshot * snapshot)1356 gtk_snapshot_pop_one (GtkSnapshot *snapshot)
1357 {
1358 GtkSnapshotState *state;
1359 guint state_index;
1360 GskRenderNode *node;
1361
1362 if (gtk_snapshot_states_is_empty (&snapshot->state_stack))
1363 {
1364 g_warning ("Too many gtk_snapshot_pop() calls.");
1365 return NULL;
1366 }
1367
1368 state = gtk_snapshot_get_current_state (snapshot);
1369 state_index = gtk_snapshot_states_get_size (&snapshot->state_stack) - 1;
1370
1371 if (state->collect_func)
1372 {
1373 node = state->collect_func (snapshot,
1374 state,
1375 (GskRenderNode **) gtk_snapshot_nodes_index (&snapshot->nodes, state->start_node_index),
1376 state->n_nodes);
1377
1378 /* The collect func may not modify the state stack... */
1379 g_assert (state_index == gtk_snapshot_states_get_size (&snapshot->state_stack) - 1);
1380
1381 /* Remove all the state's nodes from the list of nodes */
1382 g_assert (state->start_node_index + state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
1383 gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, FALSE, NULL, 0);
1384 }
1385 else
1386 {
1387 GtkSnapshotState *previous_state;
1388
1389 node = NULL;
1390
1391 /* move the nodes to the parent */
1392 previous_state = gtk_snapshot_get_previous_state (snapshot);
1393 previous_state->n_nodes += state->n_nodes;
1394 g_assert (previous_state->start_node_index + previous_state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
1395 }
1396
1397 gtk_snapshot_states_splice (&snapshot->state_stack, state_index, 1, FALSE, NULL, 0);
1398
1399 return node;
1400 }
1401
1402 static void
gtk_snapshot_append_node_internal(GtkSnapshot * snapshot,GskRenderNode * node)1403 gtk_snapshot_append_node_internal (GtkSnapshot *snapshot,
1404 GskRenderNode *node)
1405 {
1406 GtkSnapshotState *current_state;
1407
1408 current_state = gtk_snapshot_get_current_state (snapshot);
1409
1410 if (current_state)
1411 {
1412 gtk_snapshot_nodes_append (&snapshot->nodes, node);
1413 current_state->n_nodes ++;
1414 }
1415 else
1416 {
1417 g_critical ("Tried appending a node to an already finished snapshot.");
1418 }
1419 }
1420
1421 static GskRenderNode *
gtk_snapshot_pop_internal(GtkSnapshot * snapshot,gboolean is_texture_pop)1422 gtk_snapshot_pop_internal (GtkSnapshot *snapshot,
1423 gboolean is_texture_pop)
1424 {
1425 GtkSnapshotState *state;
1426 GskRenderNode *node;
1427 guint forgotten_restores = 0;
1428
1429 for (state = gtk_snapshot_get_current_state (snapshot);
1430 gtk_snapshot_state_should_autopop (state) ||
1431 state->collect_func == NULL;
1432 state = gtk_snapshot_get_current_state (snapshot))
1433 {
1434 if (state->collect_func == NULL)
1435 forgotten_restores++;
1436
1437 node = gtk_snapshot_pop_one (snapshot);
1438 if (node)
1439 gtk_snapshot_append_node_internal (snapshot, node);
1440 }
1441
1442 if (forgotten_restores)
1443 {
1444 g_warning ("Too many gtk_snapshot_save() calls. %u saves remaining.", forgotten_restores);
1445 }
1446
1447 if (is_texture_pop && (state->collect_func != gtk_snapshot_collect_gl_shader_texture))
1448 {
1449 g_critical ("Unexpected call to gtk_snapshot_gl_shader_pop_texture().");
1450 return NULL;
1451 }
1452 else if (!is_texture_pop && (state->collect_func == gtk_snapshot_collect_gl_shader_texture))
1453 {
1454 g_critical ("Expected a call to gtk_snapshot_gl_shader_pop_texture().");
1455 return NULL;
1456 }
1457
1458 return gtk_snapshot_pop_one (snapshot);
1459 }
1460
1461 /**
1462 * gtk_snapshot_push_collect:
1463 *
1464 * Private.
1465 *
1466 * Pushes state so a later pop_collect call can collect all nodes
1467 * appended until that point.
1468 */
1469 void
gtk_snapshot_push_collect(GtkSnapshot * snapshot)1470 gtk_snapshot_push_collect (GtkSnapshot *snapshot)
1471 {
1472 gtk_snapshot_push_state (snapshot,
1473 NULL,
1474 gtk_snapshot_collect_default,
1475 NULL);
1476 }
1477
1478 GskRenderNode *
gtk_snapshot_pop_collect(GtkSnapshot * snapshot)1479 gtk_snapshot_pop_collect (GtkSnapshot *snapshot)
1480 {
1481 GskRenderNode *result = gtk_snapshot_pop_internal (snapshot, FALSE);
1482
1483 return result;
1484 }
1485
1486 /**
1487 * gtk_snapshot_to_node:
1488 * @snapshot: a `GtkSnapshot`
1489 *
1490 * Returns the render node that was constructed
1491 * by @snapshot.
1492 *
1493 * After calling this function, it is no longer possible to
1494 * add more nodes to @snapshot. The only function that should
1495 * be called after this is g_object_unref().
1496 *
1497 * Returns: (transfer full): the constructed `GskRenderNode`
1498 */
1499 GskRenderNode *
gtk_snapshot_to_node(GtkSnapshot * snapshot)1500 gtk_snapshot_to_node (GtkSnapshot *snapshot)
1501 {
1502 GskRenderNode *result;
1503
1504 result = gtk_snapshot_pop_internal (snapshot, FALSE);
1505
1506 /* We should have exactly our initial state */
1507 if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
1508 {
1509 g_warning ("Too many gtk_snapshot_push() calls. %zu states remaining.",
1510 gtk_snapshot_states_get_size (&snapshot->state_stack));
1511 }
1512
1513 gtk_snapshot_states_clear (&snapshot->state_stack);
1514 gtk_snapshot_nodes_clear (&snapshot->nodes);
1515
1516 return result;
1517 }
1518
1519 /**
1520 * gtk_snapshot_to_paintable:
1521 * @snapshot: a `GtkSnapshot`
1522 * @size: (nullable): The size of the resulting paintable
1523 * or %NULL to use the bounds of the snapshot
1524 *
1525 * Returns a paintable encapsulating the render node
1526 * that was constructed by @snapshot.
1527 *
1528 * After calling this function, it is no longer possible to
1529 * add more nodes to @snapshot. The only function that should
1530 * be called after this is g_object_unref().
1531 *
1532 * Returns: (transfer full): a new `GdkPaintable`
1533 */
1534 GdkPaintable *
gtk_snapshot_to_paintable(GtkSnapshot * snapshot,const graphene_size_t * size)1535 gtk_snapshot_to_paintable (GtkSnapshot *snapshot,
1536 const graphene_size_t *size)
1537 {
1538 GskRenderNode *node;
1539 GdkPaintable *paintable;
1540 graphene_rect_t bounds;
1541
1542 node = gtk_snapshot_to_node (snapshot);
1543 if (size)
1544 {
1545 graphene_size_init_from_size (&bounds.size, size);
1546 }
1547 else
1548 {
1549 gsk_render_node_get_bounds (node, &bounds);
1550 bounds.size.width += bounds.origin.x;
1551 bounds.size.height += bounds.origin.y;
1552 }
1553 bounds.origin.x = 0;
1554 bounds.origin.y = 0;
1555
1556 paintable = gtk_render_node_paintable_new (node, &bounds);
1557 gsk_render_node_unref (node);
1558
1559 return paintable;
1560 }
1561
1562 /**
1563 * gtk_snapshot_pop:
1564 * @snapshot: a `GtkSnapshot`
1565 *
1566 * Removes the top element from the stack of render nodes,
1567 * and appends it to the node underneath it.
1568 */
1569 void
gtk_snapshot_pop(GtkSnapshot * snapshot)1570 gtk_snapshot_pop (GtkSnapshot *snapshot)
1571 {
1572 GskRenderNode *node;
1573
1574 node = gtk_snapshot_pop_internal (snapshot, FALSE);
1575
1576 if (node)
1577 gtk_snapshot_append_node_internal (snapshot, node);
1578 }
1579
1580 /**
1581 * gtk_snapshot_gl_shader_pop_texture:
1582 * @snapshot: a `GtkSnapshot`
1583 *
1584 * Removes the top element from the stack of render nodes and
1585 * adds it to the nearest `GskGLShaderNode` below it.
1586 *
1587 * This must be called the same number of times as the number
1588 * of textures is needed for the shader in
1589 * [method@Gtk.Snapshot.push_gl_shader].
1590 */
1591 void
gtk_snapshot_gl_shader_pop_texture(GtkSnapshot * snapshot)1592 gtk_snapshot_gl_shader_pop_texture (GtkSnapshot *snapshot)
1593 {
1594 G_GNUC_UNUSED GskRenderNode *node;
1595
1596 node = gtk_snapshot_pop_internal (snapshot, TRUE);
1597 g_assert (node == NULL);
1598 }
1599
1600
1601 /**
1602 * gtk_snapshot_save:
1603 * @snapshot: a `GtkSnapshot`
1604 *
1605 * Makes a copy of the current state of @snapshot and saves it
1606 * on an internal stack.
1607 *
1608 * When [method@Gtk.Snapshot.restore] is called, @snapshot will
1609 * be restored to the saved state. Multiple calls to
1610 * gtk_snapshot_save() and gtk_snapshot_restore() can be nested;
1611 * each call to gtk_snapshot_restore() restores the state from
1612 * the matching paired gtk_snapshot_save().
1613 *
1614 * It is necessary to clear all saved states with corresponding
1615 * calls to gtk_snapshot_restore().
1616 */
1617 void
gtk_snapshot_save(GtkSnapshot * snapshot)1618 gtk_snapshot_save (GtkSnapshot *snapshot)
1619 {
1620 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1621
1622 gtk_snapshot_push_state (snapshot,
1623 gtk_snapshot_get_current_state (snapshot)->transform,
1624 NULL,
1625 NULL);
1626 }
1627
1628 /**
1629 * gtk_snapshot_restore:
1630 * @snapshot: a `GtkSnapshot`
1631 *
1632 * Restores @snapshot to the state saved by a preceding call to
1633 * gtk_snapshot_save() and removes that state from the stack of
1634 * saved states.
1635 */
1636 void
gtk_snapshot_restore(GtkSnapshot * snapshot)1637 gtk_snapshot_restore (GtkSnapshot *snapshot)
1638 {
1639 GtkSnapshotState *state;
1640 GskRenderNode *node;
1641
1642 for (state = gtk_snapshot_get_current_state (snapshot);
1643 gtk_snapshot_state_should_autopop (state);
1644 state = gtk_snapshot_get_current_state (snapshot))
1645 {
1646 node = gtk_snapshot_pop_one (snapshot);
1647 if (node)
1648 gtk_snapshot_append_node_internal (snapshot, node);
1649 }
1650
1651 if (state->collect_func != NULL)
1652 {
1653 g_warning ("Too many gtk_snapshot_restore() calls.");
1654 return;
1655 }
1656
1657 node = gtk_snapshot_pop_one (snapshot);
1658 g_assert (node == NULL);
1659 }
1660
1661 /**
1662 * gtk_snapshot_transform:
1663 * @snapshot: a `GtkSnapshot`
1664 * @transform: (nullable): the transform to apply
1665 *
1666 * Transforms @snapshot's coordinate system with the given @transform.
1667 */
1668 void
gtk_snapshot_transform(GtkSnapshot * snapshot,GskTransform * transform)1669 gtk_snapshot_transform (GtkSnapshot *snapshot,
1670 GskTransform *transform)
1671 {
1672 GtkSnapshotState *state;
1673
1674 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1675
1676 state = gtk_snapshot_get_current_state (snapshot);
1677 state->transform = gsk_transform_transform (state->transform, transform);
1678 }
1679
1680 /**
1681 * gtk_snapshot_transform_matrix:
1682 * @snapshot: a `GtkSnapshot`
1683 * @matrix: the matrix to multiply the transform with
1684 *
1685 * Transforms @snapshot's coordinate system with the given @matrix.
1686 */
1687 void
gtk_snapshot_transform_matrix(GtkSnapshot * snapshot,const graphene_matrix_t * matrix)1688 gtk_snapshot_transform_matrix (GtkSnapshot *snapshot,
1689 const graphene_matrix_t *matrix)
1690 {
1691 GtkSnapshotState *state;
1692
1693 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1694 g_return_if_fail (matrix != NULL);
1695
1696 state = gtk_snapshot_get_current_state (snapshot);
1697 state->transform = gsk_transform_matrix (state->transform, matrix);
1698 }
1699
1700 /**
1701 * gtk_snapshot_translate:
1702 * @snapshot: a `GtkSnapshot`
1703 * @point: the point to translate the snapshot by
1704 *
1705 * Translates @snapshot's coordinate system by @point in 2-dimensional space.
1706 */
1707 void
gtk_snapshot_translate(GtkSnapshot * snapshot,const graphene_point_t * point)1708 gtk_snapshot_translate (GtkSnapshot *snapshot,
1709 const graphene_point_t *point)
1710 {
1711 GtkSnapshotState *state;
1712
1713 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1714 g_return_if_fail (point != NULL);
1715
1716 state = gtk_snapshot_get_current_state (snapshot);
1717 state->transform = gsk_transform_translate (state->transform, point);
1718 }
1719
1720 /**
1721 * gtk_snapshot_translate_3d:
1722 * @snapshot: a `GtkSnapshot`
1723 * @point: the point to translate the snapshot by
1724 *
1725 * Translates @snapshot's coordinate system by @point.
1726 */
1727 void
gtk_snapshot_translate_3d(GtkSnapshot * snapshot,const graphene_point3d_t * point)1728 gtk_snapshot_translate_3d (GtkSnapshot *snapshot,
1729 const graphene_point3d_t *point)
1730 {
1731 GtkSnapshotState *state;
1732
1733 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1734 g_return_if_fail (point != NULL);
1735
1736 state = gtk_snapshot_get_current_state (snapshot);
1737 state->transform = gsk_transform_translate_3d (state->transform, point);
1738 }
1739
1740 /**
1741 * gtk_snapshot_rotate:
1742 * @snapshot: a `GtkSnapshot`
1743 * @angle: the rotation angle, in degrees (clockwise)
1744 *
1745 * Rotates @@snapshot's coordinate system by @angle degrees in 2D space -
1746 * or in 3D speak, rotates around the Z axis.
1747 *
1748 * To rotate around other axes, use [method@Gsk.Transform.rotate_3d].
1749 */
1750 void
gtk_snapshot_rotate(GtkSnapshot * snapshot,float angle)1751 gtk_snapshot_rotate (GtkSnapshot *snapshot,
1752 float angle)
1753 {
1754 GtkSnapshotState *state;
1755
1756 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1757
1758 state = gtk_snapshot_get_current_state (snapshot);
1759 state->transform = gsk_transform_rotate (state->transform, angle);
1760 }
1761
1762 /**
1763 * gtk_snapshot_rotate_3d:
1764 * @snapshot: a `GtkSnapshot`
1765 * @angle: the rotation angle, in degrees (clockwise)
1766 * @axis: The rotation axis
1767 *
1768 * Rotates @snapshot's coordinate system by @angle degrees around @axis.
1769 *
1770 * For a rotation in 2D space, use [method@Gsk.Transform.rotate].
1771 */
1772 void
gtk_snapshot_rotate_3d(GtkSnapshot * snapshot,float angle,const graphene_vec3_t * axis)1773 gtk_snapshot_rotate_3d (GtkSnapshot *snapshot,
1774 float angle,
1775 const graphene_vec3_t *axis)
1776 {
1777 GtkSnapshotState *state;
1778
1779 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1780 g_return_if_fail (axis != NULL);
1781
1782 state = gtk_snapshot_get_current_state (snapshot);
1783 state->transform = gsk_transform_rotate_3d (state->transform, angle, axis);
1784 }
1785
1786 /**
1787 * gtk_snapshot_scale:
1788 * @snapshot: a `GtkSnapshot`
1789 * @factor_x: scaling factor on the X axis
1790 * @factor_y: scaling factor on the Y axis
1791 *
1792 * Scales @snapshot's coordinate system in 2-dimensional space by
1793 * the given factors.
1794 *
1795 * Use [method@Gtk.Snapshot.scale_3d] to scale in all 3 dimensions.
1796 */
1797 void
gtk_snapshot_scale(GtkSnapshot * snapshot,float factor_x,float factor_y)1798 gtk_snapshot_scale (GtkSnapshot *snapshot,
1799 float factor_x,
1800 float factor_y)
1801 {
1802 GtkSnapshotState *state;
1803
1804 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1805
1806 state = gtk_snapshot_get_current_state (snapshot);
1807 state->transform = gsk_transform_scale (state->transform, factor_x, factor_y);
1808 }
1809
1810 /**
1811 * gtk_snapshot_scale_3d:
1812 * @snapshot: a `GtkSnapshot`
1813 * @factor_x: scaling factor on the X axis
1814 * @factor_y: scaling factor on the Y axis
1815 * @factor_z: scaling factor on the Z axis
1816 *
1817 * Scales @snapshot's coordinate system by the given factors.
1818 */
1819 void
gtk_snapshot_scale_3d(GtkSnapshot * snapshot,float factor_x,float factor_y,float factor_z)1820 gtk_snapshot_scale_3d (GtkSnapshot *snapshot,
1821 float factor_x,
1822 float factor_y,
1823 float factor_z)
1824 {
1825 GtkSnapshotState *state;
1826
1827 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1828
1829 state = gtk_snapshot_get_current_state (snapshot);
1830 state->transform = gsk_transform_scale_3d (state->transform, factor_x, factor_y, factor_z);
1831 }
1832
1833 /**
1834 * gtk_snapshot_perspective:
1835 * @snapshot: a `GtkSnapshot`
1836 * @depth: distance of the z=0 plane
1837 *
1838 * Applies a perspective projection transform.
1839 *
1840 * See [method@Gsk.Transform.perspective] for a discussion on the details.
1841 */
1842 void
gtk_snapshot_perspective(GtkSnapshot * snapshot,float depth)1843 gtk_snapshot_perspective (GtkSnapshot *snapshot,
1844 float depth)
1845 {
1846 GtkSnapshotState *state;
1847
1848 g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
1849
1850 state = gtk_snapshot_get_current_state (snapshot);
1851 state->transform = gsk_transform_perspective (state->transform, depth);
1852 }
1853
1854 /**
1855 * gtk_snapshot_append_node:
1856 * @snapshot: a `GtkSnapshot`
1857 * @node: a `GskRenderNode`
1858 *
1859 * Appends @node to the current render node of @snapshot,
1860 * without changing the current node.
1861 *
1862 * If @snapshot does not have a current node yet, @node
1863 * will become the initial node.
1864 */
1865 void
gtk_snapshot_append_node(GtkSnapshot * snapshot,GskRenderNode * node)1866 gtk_snapshot_append_node (GtkSnapshot *snapshot,
1867 GskRenderNode *node)
1868 {
1869 g_return_if_fail (snapshot != NULL);
1870 g_return_if_fail (GSK_IS_RENDER_NODE (node));
1871
1872 gtk_snapshot_ensure_identity (snapshot);
1873
1874 gtk_snapshot_append_node_internal (snapshot, gsk_render_node_ref (node));
1875 }
1876
1877 /**
1878 * gtk_snapshot_append_cairo:
1879 * @snapshot: a `GtkSnapshot`
1880 * @bounds: the bounds for the new node
1881 *
1882 * Creates a new `GskCairoNode` and appends it to the current
1883 * render node of @snapshot, without changing the current node.
1884 *
1885 * Returns: a `cairo_t` suitable for drawing the contents of
1886 * the newly created render node
1887 */
1888 cairo_t *
gtk_snapshot_append_cairo(GtkSnapshot * snapshot,const graphene_rect_t * bounds)1889 gtk_snapshot_append_cairo (GtkSnapshot *snapshot,
1890 const graphene_rect_t *bounds)
1891 {
1892 GskRenderNode *node;
1893 graphene_rect_t real_bounds;
1894 float scale_x, scale_y, dx, dy;
1895 cairo_t *cr;
1896
1897 g_return_val_if_fail (snapshot != NULL, NULL);
1898 g_return_val_if_fail (bounds != NULL, NULL);
1899
1900 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
1901 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
1902
1903 node = gsk_cairo_node_new (&real_bounds);
1904
1905 gtk_snapshot_append_node_internal (snapshot, node);
1906
1907 cr = gsk_cairo_node_get_draw_context (node);
1908
1909 cairo_scale (cr, scale_x, scale_y);
1910 cairo_translate (cr, dx, dy);
1911
1912 return cr;
1913 }
1914
1915 /**
1916 * gtk_snapshot_append_texture:
1917 * @snapshot: a `GtkSnapshot`
1918 * @texture: the `GdkTexture` to render
1919 * @bounds: the bounds for the new node
1920 *
1921 * Creates a new render node drawing the @texture
1922 * into the given @bounds and appends it to the
1923 * current render node of @snapshot.
1924 */
1925 void
gtk_snapshot_append_texture(GtkSnapshot * snapshot,GdkTexture * texture,const graphene_rect_t * bounds)1926 gtk_snapshot_append_texture (GtkSnapshot *snapshot,
1927 GdkTexture *texture,
1928 const graphene_rect_t *bounds)
1929 {
1930 GskRenderNode *node;
1931 graphene_rect_t real_bounds;
1932 float scale_x, scale_y, dx, dy;
1933
1934 g_return_if_fail (snapshot != NULL);
1935 g_return_if_fail (GDK_IS_TEXTURE (texture));
1936 g_return_if_fail (bounds != NULL);
1937
1938 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
1939 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
1940 node = gsk_texture_node_new (texture, &real_bounds);
1941
1942 gtk_snapshot_append_node_internal (snapshot, node);
1943 }
1944
1945 /**
1946 * gtk_snapshot_append_color:
1947 * @snapshot: a `GtkSnapshot`
1948 * @color: the `GdkRGBA` to draw
1949 * @bounds: the bounds for the new node
1950 *
1951 * Creates a new render node drawing the @color into the
1952 * given @bounds and appends it to the current render node
1953 * of @snapshot.
1954 *
1955 * You should try to avoid calling this function if
1956 * @color is transparent.
1957 */
1958 void
gtk_snapshot_append_color(GtkSnapshot * snapshot,const GdkRGBA * color,const graphene_rect_t * bounds)1959 gtk_snapshot_append_color (GtkSnapshot *snapshot,
1960 const GdkRGBA *color,
1961 const graphene_rect_t *bounds)
1962 {
1963 GskRenderNode *node;
1964 graphene_rect_t real_bounds;
1965 float scale_x, scale_y, dx, dy;
1966
1967 g_return_if_fail (snapshot != NULL);
1968 g_return_if_fail (color != NULL);
1969 g_return_if_fail (bounds != NULL);
1970
1971 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
1972 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
1973
1974 node = gsk_color_node_new (color, &real_bounds);
1975
1976 gtk_snapshot_append_node_internal (snapshot, node);
1977 }
1978
1979 /**
1980 * gtk_snapshot_render_background:
1981 * @snapshot: a `GtkSnapshot`
1982 * @context: the `GtkStyleContext` to use
1983 * @x: X origin of the rectangle
1984 * @y: Y origin of the rectangle
1985 * @width: rectangle width
1986 * @height: rectangle height
1987 *
1988 * Creates a render node for the CSS background according to @context,
1989 * and appends it to the current node of @snapshot, without changing
1990 * the current node.
1991 */
1992 void
gtk_snapshot_render_background(GtkSnapshot * snapshot,GtkStyleContext * context,double x,double y,double width,double height)1993 gtk_snapshot_render_background (GtkSnapshot *snapshot,
1994 GtkStyleContext *context,
1995 double x,
1996 double y,
1997 double width,
1998 double height)
1999 {
2000 GtkCssBoxes boxes;
2001
2002 g_return_if_fail (snapshot != NULL);
2003 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2004
2005 gtk_css_boxes_init_border_box (&boxes,
2006 gtk_style_context_lookup_style (context),
2007 x, y, width, height);
2008 gtk_css_style_snapshot_background (&boxes, snapshot);
2009 }
2010
2011 /**
2012 * gtk_snapshot_render_frame:
2013 * @snapshot: a `GtkSnapshot`
2014 * @context: the `GtkStyleContext` to use
2015 * @x: X origin of the rectangle
2016 * @y: Y origin of the rectangle
2017 * @width: rectangle width
2018 * @height: rectangle height
2019 *
2020 * Creates a render node for the CSS border according to @context,
2021 * and appends it to the current node of @snapshot, without changing
2022 * the current node.
2023 */
2024 void
gtk_snapshot_render_frame(GtkSnapshot * snapshot,GtkStyleContext * context,double x,double y,double width,double height)2025 gtk_snapshot_render_frame (GtkSnapshot *snapshot,
2026 GtkStyleContext *context,
2027 double x,
2028 double y,
2029 double width,
2030 double height)
2031 {
2032 GtkCssBoxes boxes;
2033
2034 g_return_if_fail (snapshot != NULL);
2035 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2036
2037 gtk_css_boxes_init_border_box (&boxes,
2038 gtk_style_context_lookup_style (context),
2039 x, y, width, height);
2040 gtk_css_style_snapshot_border (&boxes, snapshot);
2041 }
2042
2043 /**
2044 * gtk_snapshot_render_focus:
2045 * @snapshot: a `GtkSnapshot`
2046 * @context: the `GtkStyleContext` to use
2047 * @x: X origin of the rectangle
2048 * @y: Y origin of the rectangle
2049 * @width: rectangle width
2050 * @height: rectangle height
2051 *
2052 * Creates a render node for the focus outline according to @context,
2053 * and appends it to the current node of @snapshot, without changing
2054 * the current node.
2055 */
2056 void
gtk_snapshot_render_focus(GtkSnapshot * snapshot,GtkStyleContext * context,double x,double y,double width,double height)2057 gtk_snapshot_render_focus (GtkSnapshot *snapshot,
2058 GtkStyleContext *context,
2059 double x,
2060 double y,
2061 double width,
2062 double height)
2063 {
2064 GtkCssBoxes boxes;
2065
2066 g_return_if_fail (snapshot != NULL);
2067 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2068
2069 gtk_css_boxes_init_border_box (&boxes,
2070 gtk_style_context_lookup_style (context),
2071 x, y, width, height);
2072 gtk_css_style_snapshot_outline (&boxes, snapshot);
2073 }
2074
2075 /**
2076 * gtk_snapshot_render_layout:
2077 * @snapshot: a `GtkSnapshot`
2078 * @context: the `GtkStyleContext` to use
2079 * @x: X origin of the rectangle
2080 * @y: Y origin of the rectangle
2081 * @layout: the `PangoLayout` to render
2082 *
2083 * Creates a render node for rendering @layout according to the style
2084 * information in @context, and appends it to the current node of @snapshot,
2085 * without changing the current node.
2086 */
2087 void
gtk_snapshot_render_layout(GtkSnapshot * snapshot,GtkStyleContext * context,double x,double y,PangoLayout * layout)2088 gtk_snapshot_render_layout (GtkSnapshot *snapshot,
2089 GtkStyleContext *context,
2090 double x,
2091 double y,
2092 PangoLayout *layout)
2093 {
2094 const bool needs_translate = (x != 0 || y != 0);
2095 const GdkRGBA *fg_color;
2096 GtkCssValue *shadows_value;
2097 gboolean has_shadow;
2098
2099 g_return_if_fail (snapshot != NULL);
2100 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2101 g_return_if_fail (PANGO_IS_LAYOUT (layout));
2102
2103 if (needs_translate)
2104 {
2105 gtk_snapshot_save (snapshot);
2106 gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
2107 }
2108
2109 fg_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR));
2110
2111 shadows_value = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_TEXT_SHADOW);
2112 has_shadow = gtk_css_shadow_value_push_snapshot (shadows_value, snapshot);
2113
2114 gtk_snapshot_append_layout (snapshot, layout, fg_color);
2115
2116 if (has_shadow)
2117 gtk_snapshot_pop (snapshot);
2118
2119 if (needs_translate)
2120 gtk_snapshot_restore (snapshot);
2121 }
2122
2123 void
gtk_snapshot_append_text(GtkSnapshot * snapshot,PangoFont * font,PangoGlyphString * glyphs,const GdkRGBA * color,float x,float y)2124 gtk_snapshot_append_text (GtkSnapshot *snapshot,
2125 PangoFont *font,
2126 PangoGlyphString *glyphs,
2127 const GdkRGBA *color,
2128 float x,
2129 float y)
2130 {
2131 GskRenderNode *node;
2132 float dx, dy;
2133
2134 gtk_snapshot_ensure_translate (snapshot, &dx, &dy);
2135
2136 node = gsk_text_node_new (font,
2137 glyphs,
2138 color,
2139 &GRAPHENE_POINT_INIT (x + dx, y + dy));
2140 if (node == NULL)
2141 return;
2142
2143 gtk_snapshot_append_node_internal (snapshot, node);
2144 }
2145
2146 /**
2147 * gtk_snapshot_append_linear_gradient:
2148 * @snapshot: a `GtkSnapshot`
2149 * @bounds: the rectangle to render the linear gradient into
2150 * @start_point: the point at which the linear gradient will begin
2151 * @end_point: the point at which the linear gradient will finish
2152 * @stops: (array length=n_stops): a pointer to an array of `GskColorStop`
2153 * defining the gradient
2154 * @n_stops: the number of elements in @stops
2155 *
2156 * Appends a linear gradient node with the given stops to @snapshot.
2157 */
2158 void
gtk_snapshot_append_linear_gradient(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_point_t * start_point,const graphene_point_t * end_point,const GskColorStop * stops,gsize n_stops)2159 gtk_snapshot_append_linear_gradient (GtkSnapshot *snapshot,
2160 const graphene_rect_t *bounds,
2161 const graphene_point_t *start_point,
2162 const graphene_point_t *end_point,
2163 const GskColorStop *stops,
2164 gsize n_stops)
2165 {
2166 GskRenderNode *node;
2167 graphene_rect_t real_bounds;
2168 float scale_x, scale_y, dx, dy;
2169 const GdkRGBA *first_color;
2170 gboolean need_gradient = FALSE;
2171
2172 g_return_if_fail (snapshot != NULL);
2173 g_return_if_fail (start_point != NULL);
2174 g_return_if_fail (end_point != NULL);
2175 g_return_if_fail (stops != NULL);
2176 g_return_if_fail (n_stops > 1);
2177
2178 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
2179 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
2180
2181 first_color = &stops[0].color;
2182 for (gsize i = 0; i < n_stops; i ++)
2183 {
2184 if (!gdk_rgba_equal (first_color, &stops[i].color))
2185 {
2186 need_gradient = TRUE;
2187 break;
2188 }
2189 }
2190
2191 if (need_gradient)
2192 {
2193 graphene_point_t real_start_point, real_end_point;
2194
2195 real_start_point.x = scale_x * start_point->x + dx;
2196 real_start_point.y = scale_y * start_point->y + dy;
2197 real_end_point.x = scale_x * end_point->x + dx;
2198 real_end_point.y = scale_y * end_point->y + dy;
2199
2200 node = gsk_linear_gradient_node_new (&real_bounds,
2201 &real_start_point,
2202 &real_end_point,
2203 stops,
2204 n_stops);
2205 }
2206 else
2207 {
2208 node = gsk_color_node_new (first_color, &real_bounds);
2209 }
2210
2211 gtk_snapshot_append_node_internal (snapshot, node);
2212 }
2213
2214 /**
2215 * gtk_snapshot_append_repeating_linear_gradient:
2216 * @snapshot: a `GtkSnapshot`
2217 * @bounds: the rectangle to render the linear gradient into
2218 * @start_point: the point at which the linear gradient will begin
2219 * @end_point: the point at which the linear gradient will finish
2220 * @stops: (array length=n_stops): a pointer to an array of `GskColorStop`
2221 * defining the gradient
2222 * @n_stops: the number of elements in @stops
2223 *
2224 * Appends a repeating linear gradient node with the given stops to @snapshot.
2225 */
2226 void
gtk_snapshot_append_repeating_linear_gradient(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_point_t * start_point,const graphene_point_t * end_point,const GskColorStop * stops,gsize n_stops)2227 gtk_snapshot_append_repeating_linear_gradient (GtkSnapshot *snapshot,
2228 const graphene_rect_t *bounds,
2229 const graphene_point_t *start_point,
2230 const graphene_point_t *end_point,
2231 const GskColorStop *stops,
2232 gsize n_stops)
2233 {
2234 GskRenderNode *node;
2235 graphene_rect_t real_bounds;
2236 float scale_x, scale_y, dx, dy;
2237 gboolean need_gradient = FALSE;
2238 const GdkRGBA *first_color;
2239
2240 g_return_if_fail (snapshot != NULL);
2241 g_return_if_fail (start_point != NULL);
2242 g_return_if_fail (end_point != NULL);
2243 g_return_if_fail (stops != NULL);
2244 g_return_if_fail (n_stops > 1);
2245
2246 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
2247 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
2248
2249 first_color = &stops[0].color;
2250 for (gsize i = 0; i < n_stops; i ++)
2251 {
2252 if (!gdk_rgba_equal (first_color, &stops[i].color))
2253 {
2254 need_gradient = TRUE;
2255 break;
2256 }
2257 }
2258
2259 if (need_gradient)
2260 {
2261 graphene_point_t real_start_point, real_end_point;
2262
2263 real_start_point.x = scale_x * start_point->x + dx;
2264 real_start_point.y = scale_y * start_point->y + dy;
2265 real_end_point.x = scale_x * end_point->x + dx;
2266 real_end_point.y = scale_y * end_point->y + dy;
2267
2268 node = gsk_repeating_linear_gradient_node_new (&real_bounds,
2269 &real_start_point,
2270 &real_end_point,
2271 stops,
2272 n_stops);
2273 }
2274 else
2275 {
2276 node = gsk_color_node_new (first_color, &real_bounds);
2277 }
2278
2279 gtk_snapshot_append_node_internal (snapshot, node);
2280 }
2281
2282 /**
2283 * gtk_snapshot_append_conic_gradient:
2284 * @snapshot: a `GtkSnapshot`
2285 * @bounds: the rectangle to render the gradient into
2286 * @center: the center point of the conic gradient
2287 * @rotation: the clockwise rotation in degrees of the starting angle.
2288 * 0 means the starting angle is the top.
2289 * @stops: (array length=n_stops): a pointer to an array of `GskColorStop`
2290 * defining the gradient
2291 * @n_stops: the number of elements in @stops
2292 *
2293 * Appends a conic gradient node with the given stops to @snapshot.
2294 */
2295 void
gtk_snapshot_append_conic_gradient(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_point_t * center,float rotation,const GskColorStop * stops,gsize n_stops)2296 gtk_snapshot_append_conic_gradient (GtkSnapshot *snapshot,
2297 const graphene_rect_t *bounds,
2298 const graphene_point_t *center,
2299 float rotation,
2300 const GskColorStop *stops,
2301 gsize n_stops)
2302 {
2303 GskRenderNode *node;
2304 graphene_rect_t real_bounds;
2305 float dx, dy;
2306 const GdkRGBA *first_color;
2307 gboolean need_gradient = FALSE;
2308 int i;
2309
2310 g_return_if_fail (snapshot != NULL);
2311 g_return_if_fail (center != NULL);
2312 g_return_if_fail (stops != NULL);
2313 g_return_if_fail (n_stops > 1);
2314
2315 gtk_snapshot_ensure_translate (snapshot, &dx, &dy);
2316 graphene_rect_offset_r (bounds, dx, dy, &real_bounds);
2317
2318 first_color = &stops[0].color;
2319 for (i = 0; i < n_stops; i ++)
2320 {
2321 if (!gdk_rgba_equal (first_color, &stops[i].color))
2322 {
2323 need_gradient = TRUE;
2324 break;
2325 }
2326 }
2327
2328 if (need_gradient)
2329 node = gsk_conic_gradient_node_new (&real_bounds,
2330 &GRAPHENE_POINT_INIT(
2331 center->x + dx,
2332 center->y + dy
2333 ),
2334 rotation,
2335 stops,
2336 n_stops);
2337 else
2338 node = gsk_color_node_new (first_color, &real_bounds);
2339
2340 gtk_snapshot_append_node_internal (snapshot, node);
2341 }
2342
2343 /**
2344 * gtk_snapshot_append_radial_gradient:
2345 * @snapshot: a `GtkSnapshot`
2346 * @bounds: the rectangle to render the readial gradient into
2347 * @center: the center point for the radial gradient
2348 * @hradius: the horizontal radius
2349 * @vradius: the vertical radius
2350 * @start: the start position (on the horizontal axis)
2351 * @end: the end position (on the horizontal axis)
2352 * @stops: (array length=n_stops): a pointer to an array of `GskColorStop`
2353 * defining the gradient
2354 * @n_stops: the number of elements in @stops
2355 *
2356 * Appends a radial gradient node with the given stops to @snapshot.
2357 */
2358 void
gtk_snapshot_append_radial_gradient(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_point_t * center,float hradius,float vradius,float start,float end,const GskColorStop * stops,gsize n_stops)2359 gtk_snapshot_append_radial_gradient (GtkSnapshot *snapshot,
2360 const graphene_rect_t *bounds,
2361 const graphene_point_t *center,
2362 float hradius,
2363 float vradius,
2364 float start,
2365 float end,
2366 const GskColorStop *stops,
2367 gsize n_stops)
2368 {
2369 GskRenderNode *node;
2370 graphene_rect_t real_bounds;
2371 float scale_x, scale_y, dx, dy;
2372 gboolean need_gradient = FALSE;
2373 const GdkRGBA *first_color;
2374
2375 g_return_if_fail (snapshot != NULL);
2376 g_return_if_fail (center != NULL);
2377 g_return_if_fail (stops != NULL);
2378 g_return_if_fail (n_stops > 1);
2379
2380 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
2381 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
2382
2383 first_color = &stops[0].color;
2384 for (gsize i = 0; i < n_stops; i ++)
2385 {
2386 if (!gdk_rgba_equal (first_color, &stops[i].color))
2387 {
2388 need_gradient = TRUE;
2389 break;
2390 }
2391 }
2392
2393 if (need_gradient)
2394 {
2395 graphene_point_t real_center;
2396
2397 real_center.x = scale_x * center->x + dx;
2398 real_center.y = scale_y * center->y + dy;
2399
2400 node = gsk_radial_gradient_node_new (&real_bounds,
2401 &real_center,
2402 hradius * scale_x,
2403 vradius * scale_y,
2404 start,
2405 end,
2406 stops,
2407 n_stops);
2408 }
2409 else
2410 {
2411 node = gsk_color_node_new (first_color, &real_bounds);
2412 }
2413
2414 gtk_snapshot_append_node_internal (snapshot, node);
2415 }
2416
2417 /**
2418 * gtk_snapshot_append_repeating_radial_gradient:
2419 * @snapshot: a `GtkSnapshot`
2420 * @bounds: the rectangle to render the readial gradient into
2421 * @center: the center point for the radial gradient
2422 * @hradius: the horizontal radius
2423 * @vradius: the vertical radius
2424 * @start: the start position (on the horizontal axis)
2425 * @end: the end position (on the horizontal axis)
2426 * @stops: (array length=n_stops): a pointer to an array of `GskColorStop`
2427 * defining the gradient
2428 * @n_stops: the number of elements in @stops
2429 *
2430 * Appends a repeating radial gradient node with the given stops to @snapshot.
2431 */
2432 void
gtk_snapshot_append_repeating_radial_gradient(GtkSnapshot * snapshot,const graphene_rect_t * bounds,const graphene_point_t * center,float hradius,float vradius,float start,float end,const GskColorStop * stops,gsize n_stops)2433 gtk_snapshot_append_repeating_radial_gradient (GtkSnapshot *snapshot,
2434 const graphene_rect_t *bounds,
2435 const graphene_point_t *center,
2436 float hradius,
2437 float vradius,
2438 float start,
2439 float end,
2440 const GskColorStop *stops,
2441 gsize n_stops)
2442 {
2443 GskRenderNode *node;
2444 graphene_rect_t real_bounds;
2445 float scale_x, scale_y, dx, dy;
2446 gboolean need_gradient = FALSE;
2447 const GdkRGBA *first_color;
2448
2449 g_return_if_fail (snapshot != NULL);
2450 g_return_if_fail (center != NULL);
2451 g_return_if_fail (stops != NULL);
2452 g_return_if_fail (n_stops > 1);
2453
2454 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
2455 gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
2456
2457 first_color = &stops[0].color;
2458 for (gsize i = 0; i < n_stops; i ++)
2459 {
2460 if (!gdk_rgba_equal (first_color, &stops[i].color))
2461 {
2462 need_gradient = TRUE;
2463 break;
2464 }
2465 }
2466
2467 if (need_gradient)
2468 {
2469 graphene_point_t real_center;
2470
2471 real_center.x = scale_x * center->x + dx;
2472 real_center.y = scale_y * center->y + dy;
2473 node = gsk_repeating_radial_gradient_node_new (&real_bounds,
2474 &real_center,
2475 hradius * scale_x,
2476 vradius * scale_y,
2477 start,
2478 end,
2479 stops,
2480 n_stops);
2481 }
2482 else
2483 {
2484 node = gsk_color_node_new (first_color, &real_bounds);
2485 }
2486
2487 gtk_snapshot_append_node_internal (snapshot, node);
2488 }
2489
2490 /**
2491 * gtk_snapshot_append_border:
2492 * @snapshot: a `GtkSnapshot`
2493 * @outline: a `GskRoundedRect` describing the outline of the border
2494 * @border_width: (array fixed-size=4): the stroke width of the border on
2495 * the top, right, bottom and left side respectively.
2496 * @border_color: (array fixed-size=4): the color used on the top, right,
2497 * bottom and left side.
2498 *
2499 * Appends a stroked border rectangle inside the given @outline.
2500 *
2501 * The four sides of the border can have different widths and colors.
2502 */
2503 void
gtk_snapshot_append_border(GtkSnapshot * snapshot,const GskRoundedRect * outline,const float border_width[4],const GdkRGBA border_color[4])2504 gtk_snapshot_append_border (GtkSnapshot *snapshot,
2505 const GskRoundedRect *outline,
2506 const float border_width[4],
2507 const GdkRGBA border_color[4])
2508 {
2509 GskRenderNode *node;
2510 GskRoundedRect real_outline;
2511 float scale_x, scale_y, dx, dy;
2512
2513 g_return_if_fail (snapshot != NULL);
2514 g_return_if_fail (outline != NULL);
2515 g_return_if_fail (border_width != NULL);
2516 g_return_if_fail (border_color != NULL);
2517
2518 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
2519 gsk_rounded_rect_scale_affine (&real_outline, outline, scale_x, scale_y, dx, dy);
2520
2521 node = gsk_border_node_new (&real_outline,
2522 (float[4]) {
2523 border_width[0] * scale_y,
2524 border_width[1] * scale_x,
2525 border_width[2] * scale_y,
2526 border_width[3] * scale_x,
2527 },
2528 border_color);
2529
2530 gtk_snapshot_append_node_internal (snapshot, node);
2531 }
2532
2533 /**
2534 * gtk_snapshot_append_inset_shadow:
2535 * @snapshot: a `GtkSnapshot`
2536 * @outline: outline of the region surrounded by shadow
2537 * @color: color of the shadow
2538 * @dx: horizontal offset of shadow
2539 * @dy: vertical offset of shadow
2540 * @spread: how far the shadow spreads towards the inside
2541 * @blur_radius: how much blur to apply to the shadow
2542 *
2543 * Appends an inset shadow into the box given by @outline.
2544 */
2545 void
gtk_snapshot_append_inset_shadow(GtkSnapshot * snapshot,const GskRoundedRect * outline,const GdkRGBA * color,float dx,float dy,float spread,float blur_radius)2546 gtk_snapshot_append_inset_shadow (GtkSnapshot *snapshot,
2547 const GskRoundedRect *outline,
2548 const GdkRGBA *color,
2549 float dx,
2550 float dy,
2551 float spread,
2552 float blur_radius)
2553 {
2554 GskRenderNode *node;
2555 GskRoundedRect real_outline;
2556 float scale_x, scale_y, x, y;
2557
2558 g_return_if_fail (snapshot != NULL);
2559 g_return_if_fail (outline != NULL);
2560 g_return_if_fail (color != NULL);
2561
2562 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &x, &y);
2563 gsk_rounded_rect_scale_affine (&real_outline, outline, scale_x, scale_y, x, y);
2564
2565 node = gsk_inset_shadow_node_new (&real_outline,
2566 color,
2567 scale_x * dx,
2568 scale_y * dy,
2569 spread,
2570 blur_radius);
2571
2572 gtk_snapshot_append_node_internal (snapshot, node);
2573 }
2574
2575 /**
2576 * gtk_snapshot_append_outset_shadow:
2577 * @snapshot: a `GtkSnapshot`
2578 * @outline: outline of the region surrounded by shadow
2579 * @color: color of the shadow
2580 * @dx: horizontal offset of shadow
2581 * @dy: vertical offset of shadow
2582 * @spread: how far the shadow spreads towards the outside
2583 * @blur_radius: how much blur to apply to the shadow
2584 *
2585 * Appends an outset shadow node around the box given by @outline.
2586 */
2587 void
gtk_snapshot_append_outset_shadow(GtkSnapshot * snapshot,const GskRoundedRect * outline,const GdkRGBA * color,float dx,float dy,float spread,float blur_radius)2588 gtk_snapshot_append_outset_shadow (GtkSnapshot *snapshot,
2589 const GskRoundedRect *outline,
2590 const GdkRGBA *color,
2591 float dx,
2592 float dy,
2593 float spread,
2594 float blur_radius)
2595 {
2596 GskRenderNode *node;
2597 GskRoundedRect real_outline;
2598 float scale_x, scale_y, x, y;
2599
2600 g_return_if_fail (snapshot != NULL);
2601 g_return_if_fail (outline != NULL);
2602 g_return_if_fail (color != NULL);
2603
2604 gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &x, &y);
2605 gsk_rounded_rect_scale_affine (&real_outline, outline, scale_x, scale_y, x, y);
2606
2607 node = gsk_outset_shadow_node_new (&real_outline,
2608 color,
2609 scale_x * dx,
2610 scale_y * dy,
2611 spread,
2612 blur_radius);
2613
2614
2615 gtk_snapshot_append_node_internal (snapshot, node);
2616 }
2617