1 | /* GSK - The GTK Scene Kit |
2 | * |
3 | * Copyright 2016 Endless |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | /** |
20 | * GskRenderNode: (ref-func gsk_render_node_ref) (unref-func gsk_render_node_unref) (set-value-func gsk_value_set_render_node) (get-value-func gsk_value_get_render_node) |
21 | * |
22 | * `GskRenderNode` is the basic block in a scene graph to be |
23 | * rendered using [class@Gsk.Renderer]. |
24 | * |
25 | * Each node has a parent, except the top-level node; each node may have |
26 | * children nodes. |
27 | * |
28 | * Each node has an associated drawing surface, which has the size of |
29 | * the rectangle set when creating it. |
30 | * |
31 | * Render nodes are meant to be transient; once they have been associated |
32 | * to a [class@Gsk.Renderer] it's safe to release any reference you have on |
33 | * them. All [class@Gsk.RenderNode]s are immutable, you can only specify their |
34 | * properties during construction. |
35 | */ |
36 | |
37 | #include "config.h" |
38 | |
39 | #include "gskrendernodeprivate.h" |
40 | |
41 | #include "gskdebugprivate.h" |
42 | #include "gskrendererprivate.h" |
43 | #include "gskrendernodeparserprivate.h" |
44 | |
45 | #include <graphene-gobject.h> |
46 | |
47 | #include <math.h> |
48 | |
49 | #include <gobject/gvaluecollector.h> |
50 | |
51 | G_DEFINE_QUARK (gsk-serialization-error-quark, gsk_serialization_error) |
52 | |
53 | #define GSK_RENDER_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, GskRenderNodeClass)) |
54 | |
55 | |
56 | static void |
57 | value_render_node_init (GValue *value) |
58 | { |
59 | value->data[0].v_pointer = NULL; |
60 | } |
61 | |
62 | static void |
63 | value_render_node_free_value (GValue *value) |
64 | { |
65 | if (value->data[0].v_pointer != NULL) |
66 | gsk_render_node_unref (node: value->data[0].v_pointer); |
67 | } |
68 | |
69 | static void |
70 | value_render_node_copy_value (const GValue *src, |
71 | GValue *dst) |
72 | { |
73 | if (src->data[0].v_pointer != NULL) |
74 | dst->data[0].v_pointer = gsk_render_node_ref (node: src->data[0].v_pointer); |
75 | else |
76 | dst->data[0].v_pointer = NULL; |
77 | } |
78 | |
79 | static gpointer |
80 | value_render_node_peek_pointer (const GValue *value) |
81 | { |
82 | return value->data[0].v_pointer; |
83 | } |
84 | |
85 | static char * |
86 | value_render_node_collect_value (GValue *value, |
87 | guint n_collect_values, |
88 | GTypeCValue *collect_values, |
89 | guint collect_flags) |
90 | { |
91 | GskRenderNode *node = collect_values[0].v_pointer; |
92 | |
93 | if (node == NULL) |
94 | { |
95 | value->data[0].v_pointer = NULL; |
96 | return NULL; |
97 | } |
98 | |
99 | if (node->parent_instance.g_class == NULL) |
100 | return g_strconcat (string1: "invalid unclassed GskRenderNode pointer for " |
101 | "value type '" , |
102 | G_VALUE_TYPE_NAME (value), |
103 | "'" , |
104 | NULL); |
105 | |
106 | value->data[0].v_pointer = gsk_render_node_ref (node); |
107 | |
108 | return NULL; |
109 | } |
110 | |
111 | static char * |
112 | value_render_node_lcopy_value (const GValue *value, |
113 | guint n_collect_values, |
114 | GTypeCValue *collect_values, |
115 | guint collect_flags) |
116 | { |
117 | GskRenderNode **node_p = collect_values[0].v_pointer; |
118 | |
119 | if (node_p == NULL) |
120 | return g_strconcat (string1: "value location for '" , |
121 | G_VALUE_TYPE_NAME (value), |
122 | "' passed as NULL" , |
123 | NULL); |
124 | |
125 | if (value->data[0].v_pointer == NULL) |
126 | *node_p = NULL; |
127 | else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) |
128 | *node_p = value->data[0].v_pointer; |
129 | else |
130 | *node_p = gsk_render_node_ref (node: value->data[0].v_pointer); |
131 | |
132 | return NULL; |
133 | } |
134 | |
135 | static void |
136 | gsk_render_node_finalize (GskRenderNode *self) |
137 | { |
138 | g_type_free_instance (instance: (GTypeInstance *) self); |
139 | } |
140 | |
141 | static void |
142 | gsk_render_node_real_draw (GskRenderNode *node, |
143 | cairo_t *cr) |
144 | { |
145 | } |
146 | |
147 | static gboolean |
148 | gsk_render_node_real_can_diff (const GskRenderNode *node1, |
149 | const GskRenderNode *node2) |
150 | { |
151 | return FALSE; |
152 | } |
153 | |
154 | static void |
155 | gsk_render_node_real_diff (GskRenderNode *node1, |
156 | GskRenderNode *node2, |
157 | cairo_region_t *region) |
158 | { |
159 | } |
160 | |
161 | static void |
162 | gsk_render_node_class_init (GskRenderNodeClass *klass) |
163 | { |
164 | klass->node_type = GSK_NOT_A_RENDER_NODE; |
165 | klass->finalize = gsk_render_node_finalize; |
166 | klass->draw = gsk_render_node_real_draw; |
167 | klass->can_diff = gsk_render_node_real_can_diff; |
168 | klass->diff = gsk_render_node_real_diff; |
169 | } |
170 | |
171 | static void |
172 | gsk_render_node_init (GskRenderNode *self) |
173 | { |
174 | g_atomic_ref_count_init (arc: &self->ref_count); |
175 | } |
176 | |
177 | GType |
178 | gsk_render_node_get_type (void) |
179 | { |
180 | static gsize render_node_type__volatile; |
181 | |
182 | if (g_once_init_enter (&render_node_type__volatile)) |
183 | { |
184 | static const GTypeFundamentalInfo finfo = { |
185 | (G_TYPE_FLAG_CLASSED | |
186 | G_TYPE_FLAG_INSTANTIATABLE | |
187 | G_TYPE_FLAG_DERIVABLE | |
188 | G_TYPE_FLAG_DEEP_DERIVABLE), |
189 | }; |
190 | |
191 | static const GTypeValueTable value_table = { |
192 | value_render_node_init, |
193 | value_render_node_free_value, |
194 | value_render_node_copy_value, |
195 | value_render_node_peek_pointer, |
196 | "p" , |
197 | value_render_node_collect_value, |
198 | "p" , |
199 | value_render_node_lcopy_value, |
200 | }; |
201 | |
202 | const GTypeInfo node_info = { |
203 | /* Class */ |
204 | sizeof (GskRenderNodeClass), |
205 | (GBaseInitFunc) NULL, |
206 | (GBaseFinalizeFunc) NULL, |
207 | (GClassInitFunc) gsk_render_node_class_init, |
208 | (GClassFinalizeFunc) NULL, |
209 | NULL, |
210 | |
211 | /* Instance */ |
212 | sizeof (GskRenderNode), |
213 | 0, |
214 | (GInstanceInitFunc) gsk_render_node_init, |
215 | |
216 | /* GValue */ |
217 | &value_table, |
218 | }; |
219 | |
220 | GType render_node_type = |
221 | g_type_register_fundamental (type_id: g_type_fundamental_next (), |
222 | type_name: g_intern_static_string (string: "GskRenderNode" ), |
223 | info: &node_info, finfo: &finfo, |
224 | flags: G_TYPE_FLAG_ABSTRACT); |
225 | |
226 | g_once_init_leave (&render_node_type__volatile, render_node_type); |
227 | } |
228 | |
229 | return render_node_type__volatile; |
230 | } |
231 | |
232 | typedef struct |
233 | { |
234 | GskRenderNodeType node_type; |
235 | |
236 | void (* finalize) (GskRenderNode *node); |
237 | void (* draw) (GskRenderNode *node, |
238 | cairo_t *cr); |
239 | gboolean (* can_diff) (const GskRenderNode *node1, |
240 | const GskRenderNode *node2); |
241 | void (* diff) (GskRenderNode *node1, |
242 | GskRenderNode *node2, |
243 | cairo_region_t *region); |
244 | } RenderNodeClassData; |
245 | |
246 | static void |
247 | gsk_render_node_generic_class_init (gpointer g_class, |
248 | gpointer class_data) |
249 | { |
250 | GskRenderNodeClass *node_class = g_class; |
251 | RenderNodeClassData *node_data = class_data; |
252 | |
253 | /* Mandatory */ |
254 | node_class->node_type = node_data->node_type; |
255 | |
256 | /* Optional */ |
257 | if (node_data->finalize != NULL) |
258 | node_class->finalize = node_data->finalize; |
259 | if (node_data->can_diff != NULL) |
260 | node_class->can_diff = node_data->can_diff; |
261 | |
262 | /* Mandatory */ |
263 | node_class->draw = node_data->draw; |
264 | node_class->diff = node_data->diff; |
265 | |
266 | g_free (mem: node_data); |
267 | } |
268 | |
269 | static gboolean |
270 | gsk_render_node_can_diff_true (const GskRenderNode *node1, |
271 | const GskRenderNode *node2) |
272 | { |
273 | return TRUE; |
274 | } |
275 | |
276 | /*< private > |
277 | * gsk_render_node_type_register_static: |
278 | * @node_name: the name of the node |
279 | * @node_info: type information of the node |
280 | * |
281 | * Registers a new `GskRenderNode` type for the given @node_name using |
282 | * the type information in @node_info. |
283 | * |
284 | * Returns: the newly registered GType |
285 | */ |
286 | GType |
287 | gsk_render_node_type_register_static (const char *node_name, |
288 | const GskRenderNodeTypeInfo *node_info) |
289 | { |
290 | GTypeInfo info; |
291 | |
292 | info.class_size = sizeof (GskRenderNodeClass); |
293 | info.base_init = NULL; |
294 | info.base_finalize = NULL; |
295 | info.class_init = gsk_render_node_generic_class_init; |
296 | info.class_finalize = NULL; |
297 | |
298 | /* Avoid having a class_init() and a class struct for every GskRenderNode, |
299 | * by passing the various virtual functions and class data when initializing |
300 | * the base class |
301 | */ |
302 | info.class_data = g_new (RenderNodeClassData, 1); |
303 | ((RenderNodeClassData *) info.class_data)->node_type = node_info->node_type; |
304 | ((RenderNodeClassData *) info.class_data)->finalize = node_info->finalize; |
305 | ((RenderNodeClassData *) info.class_data)->draw = node_info->draw; |
306 | ((RenderNodeClassData *) info.class_data)->can_diff = node_info->can_diff != NULL |
307 | ? node_info->can_diff |
308 | : gsk_render_node_can_diff_true; |
309 | ((RenderNodeClassData *) info.class_data)->diff = node_info->diff != NULL |
310 | ? node_info->diff |
311 | : gsk_render_node_diff_impossible; |
312 | |
313 | info.instance_size = node_info->instance_size; |
314 | info.n_preallocs = 0; |
315 | info.instance_init = (GInstanceInitFunc) node_info->instance_init; |
316 | info.value_table = NULL; |
317 | |
318 | return g_type_register_static (GSK_TYPE_RENDER_NODE, type_name: node_name, info: &info, flags: 0); |
319 | } |
320 | |
321 | /*< private > |
322 | * gsk_render_node_alloc: |
323 | * @node_type: the `GskRenderNode`Type to instantiate |
324 | * |
325 | * Instantiates a new `GskRenderNode` for the given @node_type. |
326 | * |
327 | * Returns: (transfer full) (type GskRenderNode): the newly created `GskRenderNode` |
328 | */ |
329 | gpointer |
330 | gsk_render_node_alloc (GskRenderNodeType node_type) |
331 | { |
332 | g_return_val_if_fail (node_type > GSK_NOT_A_RENDER_NODE, NULL); |
333 | g_return_val_if_fail (node_type < GSK_RENDER_NODE_TYPE_N_TYPES, NULL); |
334 | |
335 | g_assert (gsk_render_node_types[node_type] != G_TYPE_INVALID); |
336 | return g_type_create_instance (type: gsk_render_node_types[node_type]); |
337 | } |
338 | |
339 | /** |
340 | * gsk_render_node_ref: |
341 | * @node: a `GskRenderNode` |
342 | * |
343 | * Acquires a reference on the given `GskRenderNode`. |
344 | * |
345 | * Returns: (transfer full): the `GskRenderNode` with an additional reference |
346 | */ |
347 | GskRenderNode * |
348 | gsk_render_node_ref (GskRenderNode *node) |
349 | { |
350 | g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); |
351 | |
352 | g_atomic_ref_count_inc (arc: &node->ref_count); |
353 | |
354 | return node; |
355 | } |
356 | |
357 | /** |
358 | * gsk_render_node_unref: |
359 | * @node: (transfer full): a `GskRenderNode` |
360 | * |
361 | * Releases a reference on the given `GskRenderNode`. |
362 | * |
363 | * If the reference was the last, the resources associated to the @node are |
364 | * freed. |
365 | */ |
366 | void |
367 | gsk_render_node_unref (GskRenderNode *node) |
368 | { |
369 | g_return_if_fail (GSK_IS_RENDER_NODE (node)); |
370 | |
371 | if (g_atomic_ref_count_dec (arc: &node->ref_count)) |
372 | GSK_RENDER_NODE_GET_CLASS (node)->finalize (node); |
373 | } |
374 | |
375 | |
376 | /** |
377 | * gsk_render_node_get_node_type: |
378 | * @node: a `GskRenderNode` |
379 | * |
380 | * Returns the type of the @node. |
381 | * |
382 | * Returns: the type of the `GskRenderNode` |
383 | */ |
384 | GskRenderNodeType |
385 | gsk_render_node_get_node_type (const GskRenderNode *node) |
386 | { |
387 | g_return_val_if_fail (GSK_IS_RENDER_NODE (node), GSK_NOT_A_RENDER_NODE); |
388 | |
389 | return GSK_RENDER_NODE_GET_CLASS (node)->node_type; |
390 | } |
391 | |
392 | G_GNUC_PURE static inline |
393 | GskRenderNodeType |
394 | _gsk_render_node_get_node_type (const GskRenderNode *node) |
395 | { |
396 | return GSK_RENDER_NODE_GET_CLASS (node)->node_type; |
397 | } |
398 | |
399 | /** |
400 | * gsk_render_node_get_bounds: |
401 | * @node: a `GskRenderNode` |
402 | * @bounds: (out caller-allocates): return location for the boundaries |
403 | * |
404 | * Retrieves the boundaries of the @node. |
405 | * |
406 | * The node will not draw outside of its boundaries. |
407 | */ |
408 | void |
409 | gsk_render_node_get_bounds (GskRenderNode *node, |
410 | graphene_rect_t *bounds) |
411 | { |
412 | g_return_if_fail (GSK_IS_RENDER_NODE (node)); |
413 | g_return_if_fail (bounds != NULL); |
414 | |
415 | graphene_rect_init_from_rect (r: bounds, src: &node->bounds); |
416 | } |
417 | |
418 | /** |
419 | * gsk_render_node_draw: |
420 | * @node: a `GskRenderNode` |
421 | * @cr: cairo context to draw to |
422 | * |
423 | * Draw the contents of @node to the given cairo context. |
424 | * |
425 | * Typically, you'll use this function to implement fallback rendering |
426 | * of `GskRenderNode`s on an intermediate Cairo context, instead of using |
427 | * the drawing context associated to a [class@Gdk.Surface]'s rendering buffer. |
428 | * |
429 | * For advanced nodes that cannot be supported using Cairo, in particular |
430 | * for nodes doing 3D operations, this function may fail. |
431 | **/ |
432 | void |
433 | gsk_render_node_draw (GskRenderNode *node, |
434 | cairo_t *cr) |
435 | { |
436 | g_return_if_fail (GSK_IS_RENDER_NODE (node)); |
437 | g_return_if_fail (cr != NULL); |
438 | g_return_if_fail (cairo_status (cr) == CAIRO_STATUS_SUCCESS); |
439 | |
440 | cairo_save (cr); |
441 | |
442 | GSK_NOTE (CAIRO, g_message ("Rendering node %s[%p]" , |
443 | g_type_name_from_instance ((GTypeInstance *) node), |
444 | node)); |
445 | |
446 | GSK_RENDER_NODE_GET_CLASS (node)->draw (node, cr); |
447 | |
448 | #ifdef G_ENABLE_DEBUG |
449 | if (GSK_DEBUG_CHECK (GEOMETRY)) |
450 | { |
451 | cairo_set_operator (cr, op: CAIRO_OPERATOR_OVER); |
452 | cairo_rectangle (cr, x: node->bounds.origin.x - 1, y: node->bounds.origin.y - 1, |
453 | width: node->bounds.size.width + 2, height: node->bounds.size.height + 2); |
454 | cairo_set_line_width (cr, width: 2); |
455 | cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0, alpha: 0.5); |
456 | cairo_stroke (cr); |
457 | } |
458 | #endif |
459 | |
460 | cairo_restore (cr); |
461 | |
462 | if (cairo_status (cr)) |
463 | { |
464 | g_warning ("drawing failure for render node %s: %s" , |
465 | g_type_name_from_instance ((GTypeInstance *) node), |
466 | cairo_status_to_string (cairo_status (cr))); |
467 | } |
468 | } |
469 | |
470 | /* |
471 | * gsk_render_node_can_diff: |
472 | * @node1: a `GskRenderNode` |
473 | * @node2: the `GskRenderNode` to compare with |
474 | * |
475 | * Checks if two render nodes can be expected to be compared via |
476 | * gsk_render_node_diff(). |
477 | * |
478 | * The node diffing algorithm uses this function to match up similar |
479 | * nodes to compare when trying to minimize the resulting region. |
480 | * |
481 | * Nodes of different type always return %FALSE here. |
482 | * |
483 | * Returns: %TRUE if @node1 and @node2 can be expected to be compared |
484 | **/ |
485 | gboolean |
486 | gsk_render_node_can_diff (const GskRenderNode *node1, |
487 | const GskRenderNode *node2) |
488 | { |
489 | if (node1 == node2) |
490 | return TRUE; |
491 | |
492 | if (_gsk_render_node_get_node_type (node: node1) == _gsk_render_node_get_node_type (node: node2)) |
493 | return GSK_RENDER_NODE_GET_CLASS (node1)->can_diff (node1, node2); |
494 | |
495 | if (_gsk_render_node_get_node_type (node: node1) == GSK_CONTAINER_NODE || |
496 | _gsk_render_node_get_node_type (node: node2) == GSK_CONTAINER_NODE) |
497 | return TRUE; |
498 | |
499 | return FALSE; |
500 | } |
501 | |
502 | static void |
503 | rectangle_init_from_graphene (cairo_rectangle_int_t *cairo, |
504 | const graphene_rect_t *graphene) |
505 | { |
506 | cairo->x = floorf (x: graphene->origin.x); |
507 | cairo->y = floorf (x: graphene->origin.y); |
508 | cairo->width = ceilf (x: graphene->origin.x + graphene->size.width) - cairo->x; |
509 | cairo->height = ceilf (x: graphene->origin.y + graphene->size.height) - cairo->y; |
510 | } |
511 | |
512 | void |
513 | gsk_render_node_diff_impossible (GskRenderNode *node1, |
514 | GskRenderNode *node2, |
515 | cairo_region_t *region) |
516 | { |
517 | cairo_rectangle_int_t rect; |
518 | |
519 | rectangle_init_from_graphene (cairo: &rect, graphene: &node1->bounds); |
520 | cairo_region_union_rectangle (dst: region, rectangle: &rect); |
521 | rectangle_init_from_graphene (cairo: &rect, graphene: &node2->bounds); |
522 | cairo_region_union_rectangle (dst: region, rectangle: &rect); |
523 | } |
524 | |
525 | /** |
526 | * gsk_render_node_diff: |
527 | * @node1: a `GskRenderNode` |
528 | * @node2: the `GskRenderNode` to compare with |
529 | * @region: a `cairo_region_t` to add the differences to |
530 | * |
531 | * Compares @node1 and @node2 trying to compute the minimal region of changes. |
532 | * |
533 | * In the worst case, this is the union of the bounds of @node1 and @node2. |
534 | * |
535 | * This function is used to compute the area that needs to be redrawn when |
536 | * the previous contents were drawn by @node1 and the new contents should |
537 | * correspond to @node2. As such, it is important that this comparison is |
538 | * faster than the time it takes to actually do the redraw. |
539 | * |
540 | * Note that the passed in @region may already contain previous results from |
541 | * previous node comparisons, so this function call will only add to it. |
542 | **/ |
543 | void |
544 | gsk_render_node_diff (GskRenderNode *node1, |
545 | GskRenderNode *node2, |
546 | cairo_region_t *region) |
547 | { |
548 | if (node1 == node2) |
549 | return; |
550 | |
551 | if (_gsk_render_node_get_node_type (node: node1) == _gsk_render_node_get_node_type (node: node2)) |
552 | GSK_RENDER_NODE_GET_CLASS (node1)->diff (node1, node2, region); |
553 | |
554 | else if (_gsk_render_node_get_node_type (node: node1) == GSK_CONTAINER_NODE) |
555 | gsk_container_node_diff_with (container: node1, other: node2, region); |
556 | else if (_gsk_render_node_get_node_type (node: node2) == GSK_CONTAINER_NODE) |
557 | gsk_container_node_diff_with (container: node2, other: node1, region); |
558 | else |
559 | gsk_render_node_diff_impossible (node1, node2, region); |
560 | } |
561 | |
562 | /** |
563 | * gsk_render_node_write_to_file: |
564 | * @node: a `GskRenderNode` |
565 | * @filename: (type filename): the file to save it to. |
566 | * @error: Return location for a potential error |
567 | * |
568 | * This function is equivalent to calling [method@Gsk.RenderNode.serialize] |
569 | * followed by [func@GLib.file_set_contents]. |
570 | * |
571 | * See those two functions for details on the arguments. |
572 | * |
573 | * It is mostly intended for use inside a debugger to quickly dump a render |
574 | * node to a file for later inspection. |
575 | * |
576 | * Returns: %TRUE if saving was successful |
577 | **/ |
578 | gboolean |
579 | gsk_render_node_write_to_file (GskRenderNode *node, |
580 | const char *filename, |
581 | GError **error) |
582 | { |
583 | GBytes *bytes; |
584 | gboolean result; |
585 | |
586 | g_return_val_if_fail (GSK_IS_RENDER_NODE (node), FALSE); |
587 | g_return_val_if_fail (filename != NULL, FALSE); |
588 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
589 | |
590 | bytes = gsk_render_node_serialize (node); |
591 | result = g_file_set_contents (filename, |
592 | contents: g_bytes_get_data (bytes, NULL), |
593 | length: g_bytes_get_size (bytes), |
594 | error); |
595 | g_bytes_unref (bytes); |
596 | |
597 | return result; |
598 | } |
599 | |
600 | /** |
601 | * gsk_render_node_deserialize: |
602 | * @bytes: the bytes containing the data |
603 | * @error_func: (nullable) (scope call): Callback on parsing errors |
604 | * @user_data: (closure error_func): user_data for @error_func |
605 | * |
606 | * Loads data previously created via [method@Gsk.RenderNode.serialize]. |
607 | * |
608 | * For a discussion of the supported format, see that function. |
609 | * |
610 | * Returns: (nullable) (transfer full): a new `GskRenderNode` |
611 | */ |
612 | GskRenderNode * |
613 | gsk_render_node_deserialize (GBytes *bytes, |
614 | GskParseErrorFunc error_func, |
615 | gpointer user_data) |
616 | { |
617 | GskRenderNode *node = NULL; |
618 | |
619 | node = gsk_render_node_deserialize_from_bytes (bytes, error_func, user_data); |
620 | |
621 | return node; |
622 | } |
623 | |
624 | /** |
625 | * gsk_value_set_render_node: |
626 | * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE` |
627 | * @node: a `GskRenderNode` |
628 | * |
629 | * Stores the given `GskRenderNode` inside `value`. |
630 | * |
631 | * The [struct@GObject.Value] will acquire a reference to the `node`. |
632 | * |
633 | * Since: 4.6 |
634 | */ |
635 | void |
636 | gsk_value_set_render_node (GValue *value, |
637 | GskRenderNode *node) |
638 | { |
639 | GskRenderNode *old_node; |
640 | |
641 | g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE)); |
642 | |
643 | old_node = value->data[0].v_pointer; |
644 | |
645 | if (node != NULL) |
646 | { |
647 | g_return_if_fail (GSK_IS_RENDER_NODE (node)); |
648 | |
649 | value->data[0].v_pointer = gsk_render_node_ref (node); |
650 | } |
651 | else |
652 | { |
653 | value->data[0].v_pointer = NULL; |
654 | } |
655 | |
656 | if (old_node != NULL) |
657 | gsk_render_node_unref (node: old_node); |
658 | } |
659 | |
660 | /** |
661 | * gsk_value_take_render_node: |
662 | * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE` |
663 | * @node: (transfer full) (nullable): a `GskRenderNode` |
664 | * |
665 | * Stores the given `GskRenderNode` inside `value`. |
666 | * |
667 | * This function transfers the ownership of the `node` to the `GValue`. |
668 | * |
669 | * Since: 4.6 |
670 | */ |
671 | void |
672 | gsk_value_take_render_node (GValue *value, |
673 | GskRenderNode *node) |
674 | { |
675 | GskRenderNode *old_node; |
676 | |
677 | g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE)); |
678 | |
679 | old_node = value->data[0].v_pointer; |
680 | |
681 | if (node != NULL) |
682 | { |
683 | g_return_if_fail (GSK_IS_RENDER_NODE (node)); |
684 | |
685 | value->data[0].v_pointer = node; |
686 | } |
687 | else |
688 | { |
689 | value->data[0].v_pointer = NULL; |
690 | } |
691 | |
692 | if (old_node != NULL) |
693 | gsk_render_node_unref (node: old_node); |
694 | } |
695 | |
696 | /** |
697 | * gsk_value_get_render_node: |
698 | * @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE` |
699 | * |
700 | * Retrieves the `GskRenderNode` stored inside the given `value`. |
701 | * |
702 | * Returns: (transfer none) (nullable): a `GskRenderNode` |
703 | * |
704 | * Since: 4.6 |
705 | */ |
706 | GskRenderNode * |
707 | gsk_value_get_render_node (const GValue *value) |
708 | { |
709 | g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL); |
710 | |
711 | return value->data[0].v_pointer; |
712 | } |
713 | |
714 | /** |
715 | * gsk_value_dup_render_node: |
716 | * @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE` |
717 | * |
718 | * Retrieves the `GskRenderNode` stored inside the given `value`, and acquires |
719 | * a reference to it. |
720 | * |
721 | * Returns: (transfer full) (nullable): a `GskRenderNode` |
722 | * |
723 | * Since: 4.6 |
724 | */ |
725 | GskRenderNode * |
726 | gsk_value_dup_render_node (const GValue *value) |
727 | { |
728 | g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL); |
729 | |
730 | if (value->data[0].v_pointer == NULL) |
731 | return NULL; |
732 | |
733 | return gsk_render_node_ref (node: value->data[0].v_pointer); |
734 | } |
735 | |
736 | gboolean |
737 | gsk_render_node_prefers_high_depth (const GskRenderNode *node) |
738 | { |
739 | return node->prefers_high_depth; |
740 | } |
741 | |