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

source code of gtk/gtk/gtksnapshot.c