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