mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-08 17:50:10 +00:00
59f334622b
This is the most-called function on render nodes.
839 lines
23 KiB
C
839 lines
23 KiB
C
/* GSK - The GTK Scene Kit
|
|
*
|
|
* Copyright 2016 Endless
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* 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)
|
|
*
|
|
* `GskRenderNode` is the basic block in a scene graph to be
|
|
* rendered using [class@Gsk.Renderer].
|
|
*
|
|
* Each node has a parent, except the top-level node; each node may have
|
|
* children nodes.
|
|
*
|
|
* Each node has an associated drawing surface, which has the size of
|
|
* the rectangle set when creating it.
|
|
*
|
|
* Render nodes are meant to be transient; once they have been associated
|
|
* to a [class@Gsk.Renderer] it's safe to release any reference you have on
|
|
* them. All [class@Gsk.RenderNode]s are immutable, you can only specify their
|
|
* properties during construction.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gskrendernodeprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
#include "gskrendererprivate.h"
|
|
#include "gskrendernodeparserprivate.h"
|
|
|
|
#include "gdk/gdkcairoprivate.h"
|
|
#include "gdk/gdkcolorstateprivate.h"
|
|
|
|
#include <graphene-gobject.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
/**
|
|
* gsk_serialization_error_quark:
|
|
*
|
|
* Registers an error quark for [class@Gsk.RenderNode] errors.
|
|
*
|
|
* Returns: the error quark
|
|
**/
|
|
G_DEFINE_QUARK (gsk-serialization-error-quark, gsk_serialization_error)
|
|
|
|
|
|
static void
|
|
value_render_node_init (GValue *value)
|
|
{
|
|
value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
static void
|
|
value_render_node_free_value (GValue *value)
|
|
{
|
|
if (value->data[0].v_pointer != NULL)
|
|
gsk_render_node_unref (value->data[0].v_pointer);
|
|
}
|
|
|
|
static void
|
|
value_render_node_copy_value (const GValue *src,
|
|
GValue *dst)
|
|
{
|
|
if (src->data[0].v_pointer != NULL)
|
|
dst->data[0].v_pointer = gsk_render_node_ref (src->data[0].v_pointer);
|
|
else
|
|
dst->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
static gpointer
|
|
value_render_node_peek_pointer (const GValue *value)
|
|
{
|
|
return value->data[0].v_pointer;
|
|
}
|
|
|
|
static char *
|
|
value_render_node_collect_value (GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags)
|
|
{
|
|
GskRenderNode *node = collect_values[0].v_pointer;
|
|
|
|
if (node == NULL)
|
|
{
|
|
value->data[0].v_pointer = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
if (node->parent_instance.g_class == NULL)
|
|
return g_strconcat ("invalid unclassed GskRenderNode pointer for "
|
|
"value type '",
|
|
G_VALUE_TYPE_NAME (value),
|
|
"'",
|
|
NULL);
|
|
|
|
value->data[0].v_pointer = gsk_render_node_ref (node);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static char *
|
|
value_render_node_lcopy_value (const GValue *value,
|
|
guint n_collect_values,
|
|
GTypeCValue *collect_values,
|
|
guint collect_flags)
|
|
{
|
|
GskRenderNode **node_p = collect_values[0].v_pointer;
|
|
|
|
if (node_p == NULL)
|
|
return g_strconcat ("value location for '",
|
|
G_VALUE_TYPE_NAME (value),
|
|
"' passed as NULL",
|
|
NULL);
|
|
|
|
if (value->data[0].v_pointer == NULL)
|
|
*node_p = NULL;
|
|
else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
|
|
*node_p = value->data[0].v_pointer;
|
|
else
|
|
*node_p = gsk_render_node_ref (value->data[0].v_pointer);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gsk_render_node_finalize (GskRenderNode *self)
|
|
{
|
|
g_type_free_instance ((GTypeInstance *) self);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_render_node_real_can_diff (const GskRenderNode *node1,
|
|
const GskRenderNode *node2)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_render_node_real_diff (GskRenderNode *node1,
|
|
GskRenderNode *node2,
|
|
GskDiffData *data)
|
|
{
|
|
gsk_render_node_diff_impossible (node1, node2, data);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_render_node_real_get_opaque_rect (GskRenderNode *node,
|
|
graphene_rect_t *out_opaque)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gsk_render_node_class_init (GskRenderNodeClass *klass)
|
|
{
|
|
klass->node_type = GSK_NOT_A_RENDER_NODE;
|
|
klass->finalize = gsk_render_node_finalize;
|
|
klass->can_diff = gsk_render_node_real_can_diff;
|
|
klass->diff = gsk_render_node_real_diff;
|
|
klass->get_opaque_rect = gsk_render_node_real_get_opaque_rect;
|
|
}
|
|
|
|
static void
|
|
gsk_render_node_init (GskRenderNode *self)
|
|
{
|
|
g_atomic_ref_count_init (&self->ref_count);
|
|
self->preferred_depth = GDK_N_DEPTHS; /* illegal value */
|
|
}
|
|
|
|
GType
|
|
gsk_render_node_get_type (void)
|
|
{
|
|
static gsize render_node_type__volatile;
|
|
|
|
if (g_once_init_enter (&render_node_type__volatile))
|
|
{
|
|
static const GTypeFundamentalInfo finfo = {
|
|
(G_TYPE_FLAG_CLASSED |
|
|
G_TYPE_FLAG_INSTANTIATABLE |
|
|
G_TYPE_FLAG_DERIVABLE |
|
|
G_TYPE_FLAG_DEEP_DERIVABLE),
|
|
};
|
|
|
|
static const GTypeValueTable value_table = {
|
|
value_render_node_init,
|
|
value_render_node_free_value,
|
|
value_render_node_copy_value,
|
|
value_render_node_peek_pointer,
|
|
"p",
|
|
value_render_node_collect_value,
|
|
"p",
|
|
value_render_node_lcopy_value,
|
|
};
|
|
|
|
const GTypeInfo node_info = {
|
|
/* Class */
|
|
sizeof (GskRenderNodeClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) gsk_render_node_class_init,
|
|
(GClassFinalizeFunc) NULL,
|
|
NULL,
|
|
|
|
/* Instance */
|
|
sizeof (GskRenderNode),
|
|
0,
|
|
(GInstanceInitFunc) gsk_render_node_init,
|
|
|
|
/* GValue */
|
|
&value_table,
|
|
};
|
|
|
|
GType render_node_type =
|
|
g_type_register_fundamental (g_type_fundamental_next (),
|
|
g_intern_static_string ("GskRenderNode"),
|
|
&node_info, &finfo,
|
|
G_TYPE_FLAG_ABSTRACT);
|
|
|
|
g_once_init_leave (&render_node_type__volatile, render_node_type);
|
|
}
|
|
|
|
return render_node_type__volatile;
|
|
}
|
|
|
|
/*< private >
|
|
* gsk_render_node_type_register_static:
|
|
* @node_name: the name of the node
|
|
* @node_info: type information of the node
|
|
*
|
|
* Registers a new `GskRenderNode` type for the given @node_name using
|
|
* the type information in @node_info.
|
|
*
|
|
* Returns: the newly registered GType
|
|
*/
|
|
GType
|
|
gsk_render_node_type_register_static (const char *node_name,
|
|
gsize instance_size,
|
|
GClassInitFunc class_init)
|
|
{
|
|
GTypeInfo info;
|
|
|
|
info.class_size = sizeof (GskRenderNodeClass);
|
|
info.base_init = NULL;
|
|
info.base_finalize = NULL;
|
|
info.class_init = class_init;
|
|
info.class_finalize = NULL;
|
|
info.instance_size = instance_size;
|
|
info.n_preallocs = 0;
|
|
info.instance_init = NULL;
|
|
info.value_table = NULL;
|
|
|
|
return g_type_register_static (GSK_TYPE_RENDER_NODE, node_name, &info, 0);
|
|
}
|
|
|
|
/*< private >
|
|
* gsk_render_node_alloc:
|
|
* @node_type: the `GskRenderNode`Type to instantiate
|
|
*
|
|
* Instantiates a new `GskRenderNode` for the given @node_type.
|
|
*
|
|
* Returns: (transfer full) (type GskRenderNode): the newly created `GskRenderNode`
|
|
*/
|
|
gpointer
|
|
gsk_render_node_alloc (GskRenderNodeType node_type)
|
|
{
|
|
g_return_val_if_fail (node_type > GSK_NOT_A_RENDER_NODE, NULL);
|
|
g_return_val_if_fail (node_type < GSK_RENDER_NODE_TYPE_N_TYPES, NULL);
|
|
|
|
g_assert (gsk_render_node_types[node_type] != G_TYPE_INVALID);
|
|
return g_type_create_instance (gsk_render_node_types[node_type]);
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_ref:
|
|
* @node: a `GskRenderNode`
|
|
*
|
|
* Acquires a reference on the given `GskRenderNode`.
|
|
*
|
|
* Returns: (transfer full): the `GskRenderNode` with an additional reference
|
|
*/
|
|
GskRenderNode *
|
|
(gsk_render_node_ref) (GskRenderNode *node)
|
|
{
|
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
|
|
|
|
return _gsk_render_node_ref (node);
|
|
}
|
|
|
|
void
|
|
_gsk_render_node_unref (GskRenderNode *node)
|
|
{
|
|
if G_UNLIKELY (g_atomic_ref_count_dec (&node->ref_count))
|
|
GSK_RENDER_NODE_GET_CLASS (node)->finalize (node);
|
|
}
|
|
|
|
|
|
/**
|
|
* gsk_render_node_unref:
|
|
* @node: (transfer full): a `GskRenderNode`
|
|
*
|
|
* Releases a reference on the given `GskRenderNode`.
|
|
*
|
|
* If the reference was the last, the resources associated to the @node are
|
|
* freed.
|
|
*/
|
|
void
|
|
(gsk_render_node_unref) (GskRenderNode *node)
|
|
{
|
|
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
|
|
|
_gsk_render_node_unref (node);
|
|
}
|
|
|
|
|
|
/**
|
|
* gsk_render_node_get_node_type:
|
|
* @node: a `GskRenderNode`
|
|
*
|
|
* Returns the type of the @node.
|
|
*
|
|
* Returns: the type of the `GskRenderNode`
|
|
*/
|
|
GskRenderNodeType
|
|
(gsk_render_node_get_node_type) (const GskRenderNode *node)
|
|
{
|
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), GSK_NOT_A_RENDER_NODE);
|
|
|
|
return GSK_RENDER_NODE_GET_CLASS (node)->node_type;
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_get_bounds:
|
|
* @node: a `GskRenderNode`
|
|
* @bounds: (out caller-allocates): return location for the boundaries
|
|
*
|
|
* Retrieves the boundaries of the @node.
|
|
*
|
|
* The node will not draw outside of its boundaries.
|
|
*/
|
|
void
|
|
gsk_render_node_get_bounds (GskRenderNode *node,
|
|
graphene_rect_t *bounds)
|
|
{
|
|
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
|
g_return_if_fail (bounds != NULL);
|
|
|
|
graphene_rect_init_from_rect (bounds, &node->bounds);
|
|
}
|
|
|
|
void
|
|
gsk_render_node_draw_ccs (GskRenderNode *node,
|
|
cairo_t *cr,
|
|
GdkColorState *ccs)
|
|
{
|
|
/* Check that the calling function did pass a correct color state */
|
|
g_assert (ccs == gdk_color_state_get_rendering_color_state (ccs));
|
|
|
|
cairo_save (cr);
|
|
|
|
GSK_RENDER_NODE_GET_CLASS (node)->draw (node, cr, ccs);
|
|
|
|
if (GSK_DEBUG_CHECK (GEOMETRY))
|
|
{
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
|
cairo_rectangle (cr, node->bounds.origin.x - 1, node->bounds.origin.y - 1,
|
|
node->bounds.size.width + 2, node->bounds.size.height + 2);
|
|
cairo_set_line_width (cr, 2);
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
cairo_restore (cr);
|
|
|
|
if (cairo_status (cr))
|
|
{
|
|
g_warning ("drawing failure for render node %s: %s",
|
|
g_type_name_from_instance ((GTypeInstance *) node),
|
|
cairo_status_to_string (cairo_status (cr)));
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_render_node_draw_with_color_state (GskRenderNode *node,
|
|
cairo_t *cr,
|
|
GdkColorState *color_state)
|
|
{
|
|
GdkColorState *ccs;
|
|
|
|
ccs = gdk_color_state_get_rendering_color_state (color_state);
|
|
|
|
if (gdk_color_state_equal (color_state, ccs))
|
|
{
|
|
gsk_render_node_draw_ccs (node, cr, ccs);
|
|
}
|
|
else
|
|
{
|
|
cairo_save (cr);
|
|
gdk_cairo_rect (cr, &node->bounds);
|
|
cairo_clip (cr);
|
|
cairo_push_group (cr);
|
|
|
|
gsk_render_node_draw_ccs (node, cr, ccs);
|
|
gdk_cairo_surface_convert_color_state (cairo_get_group_target (cr),
|
|
ccs,
|
|
color_state);
|
|
cairo_pop_group_to_source (cr);
|
|
cairo_paint (cr);
|
|
cairo_restore (cr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_draw:
|
|
* @node: a `GskRenderNode`
|
|
* @cr: cairo context to draw to
|
|
*
|
|
* Draw the contents of @node to the given cairo context.
|
|
*
|
|
* Typically, you'll use this function to implement fallback rendering
|
|
* of `GskRenderNode`s on an intermediate Cairo context, instead of using
|
|
* the drawing context associated to a [class@Gdk.Surface]'s rendering buffer.
|
|
*
|
|
* For advanced nodes that cannot be supported using Cairo, in particular
|
|
* for nodes doing 3D operations, this function may fail.
|
|
**/
|
|
void
|
|
gsk_render_node_draw (GskRenderNode *node,
|
|
cairo_t *cr)
|
|
{
|
|
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
|
g_return_if_fail (cr != NULL);
|
|
g_return_if_fail (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
|
|
|
|
gsk_render_node_draw_with_color_state (node, cr, GDK_COLOR_STATE_SRGB);
|
|
}
|
|
|
|
/*
|
|
* gsk_render_node_draw_fallback:
|
|
* @node: a `GskRenderNode`
|
|
* @cr: cairo context to draw to
|
|
*
|
|
* Like gsk_render_node_draw(), but will overlay an error pattern if
|
|
* GSK_DEBUG=cairo is enabled.
|
|
*
|
|
* This has 2 purposes:
|
|
* 1. It allows detecting fallbacks in GPU renderers.
|
|
* 2. Application code can use it to detect where it is using Cairo
|
|
* drawing.
|
|
*
|
|
* So use this function whenever either of those cases should be detected.
|
|
*/
|
|
void
|
|
gsk_render_node_draw_fallback (GskRenderNode *node,
|
|
cairo_t *cr)
|
|
{
|
|
gsk_render_node_draw (node, cr);
|
|
|
|
if (GSK_DEBUG_CHECK (CAIRO))
|
|
{
|
|
/* pink, black
|
|
* black, pink
|
|
*/
|
|
static const guint32 fallback_pixels[] = { 0xFFFF00CC, 0xFF000000,
|
|
0xFF000000, 0xFFFF00CC };
|
|
static const guint32 cairo_pixels[] = { 0xFF9900FF, 0xFF000000,
|
|
0xFF000000, 0xFF9900FF };
|
|
const guint32 *pixels;
|
|
cairo_surface_t *surface;
|
|
|
|
cairo_save (cr);
|
|
if (GSK_RENDER_NODE_TYPE (node) == GSK_CAIRO_NODE)
|
|
pixels = cairo_pixels;
|
|
else
|
|
pixels = fallback_pixels;
|
|
surface = cairo_image_surface_create_for_data ((guchar *) pixels,
|
|
CAIRO_FORMAT_ARGB32,
|
|
2, 2,
|
|
2 * 4);
|
|
cairo_scale (cr, 10, 10);
|
|
cairo_set_source_surface (cr, surface, 0, 0);
|
|
cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
|
|
cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
|
|
cairo_paint_with_alpha (cr, 0.6);
|
|
cairo_surface_destroy (surface);
|
|
cairo_restore (cr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* gsk_render_node_can_diff:
|
|
* @node1: a `GskRenderNode`
|
|
* @node2: the `GskRenderNode` to compare with
|
|
*
|
|
* Checks if two render nodes can be expected to be compared via
|
|
* gsk_render_node_diff().
|
|
*
|
|
* The node diffing algorithm uses this function to match up similar
|
|
* nodes to compare when trying to minimize the resulting region.
|
|
*
|
|
* Nodes of different type always return %FALSE here.
|
|
*
|
|
* Returns: %TRUE if @node1 and @node2 can be expected to be compared
|
|
**/
|
|
gboolean
|
|
gsk_render_node_can_diff (const GskRenderNode *node1,
|
|
const GskRenderNode *node2)
|
|
{
|
|
if (node1 == node2)
|
|
return TRUE;
|
|
|
|
if (gsk_render_node_get_node_type (node1) == gsk_render_node_get_node_type (node2))
|
|
return GSK_RENDER_NODE_GET_CLASS (node1)->can_diff (node1, node2);
|
|
|
|
if (gsk_render_node_get_node_type (node1) == GSK_CONTAINER_NODE ||
|
|
gsk_render_node_get_node_type (node2) == GSK_CONTAINER_NODE)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
|
|
const graphene_rect_t *graphene)
|
|
{
|
|
cairo->x = floorf (graphene->origin.x);
|
|
cairo->y = floorf (graphene->origin.y);
|
|
cairo->width = ceilf (graphene->origin.x + graphene->size.width) - cairo->x;
|
|
cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
|
|
}
|
|
|
|
void
|
|
gsk_render_node_diff_impossible (GskRenderNode *node1,
|
|
GskRenderNode *node2,
|
|
GskDiffData *data)
|
|
{
|
|
cairo_rectangle_int_t rect;
|
|
|
|
rectangle_init_from_graphene (&rect, &node1->bounds);
|
|
cairo_region_union_rectangle (data->region, &rect);
|
|
rectangle_init_from_graphene (&rect, &node2->bounds);
|
|
cairo_region_union_rectangle (data->region, &rect);
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_diff:
|
|
* @node1: a `GskRenderNode`
|
|
* @node2: the `GskRenderNode` to compare with
|
|
* @data: the diff data to use
|
|
*
|
|
* Compares @node1 and @node2 trying to compute the minimal region of changes.
|
|
*
|
|
* In the worst case, this is the union of the bounds of @node1 and @node2.
|
|
*
|
|
* This function is used to compute the area that needs to be redrawn when
|
|
* the previous contents were drawn by @node1 and the new contents should
|
|
* correspond to @node2. As such, it is important that this comparison is
|
|
* faster than the time it takes to actually do the redraw.
|
|
*
|
|
* Note that the passed in @region may already contain previous results from
|
|
* previous node comparisons, so this function call will only add to it.
|
|
*/
|
|
void
|
|
gsk_render_node_diff (GskRenderNode *node1,
|
|
GskRenderNode *node2,
|
|
GskDiffData *data)
|
|
{
|
|
if (node1 == node2)
|
|
return;
|
|
|
|
if (gsk_render_node_get_node_type (node1) == gsk_render_node_get_node_type (node2))
|
|
{
|
|
GSK_RENDER_NODE_GET_CLASS (node1)->diff (node1, node2, data);
|
|
}
|
|
else if (gsk_render_node_get_node_type (node1) == GSK_CONTAINER_NODE)
|
|
{
|
|
gsk_container_node_diff_with (node1, node2, data);
|
|
}
|
|
else if (gsk_render_node_get_node_type (node2) == GSK_CONTAINER_NODE)
|
|
{
|
|
gsk_container_node_diff_with (node2, node1, data);
|
|
}
|
|
else
|
|
{
|
|
gsk_render_node_diff_impossible (node1, node2, data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_get_opaque_rect:
|
|
* @self: a `GskRenderNode`
|
|
* @out_opaque: (out): return location for the opaque rect
|
|
*
|
|
* Gets an opaque rectangle inside the node that GTK can determine to
|
|
* be fully opaque.
|
|
*
|
|
* There is no guarantee that this is indeed the largest opaque rectangle or
|
|
* that regions outside the rectangle are not opaque. This function is a best
|
|
* effort with that goal.
|
|
*
|
|
* The rectangle will be fully contained in the bounds of the node.
|
|
*
|
|
* Returns: %TRUE if part or all of the rendernode is opaque, %FALSE if no
|
|
* opaque region could be found.
|
|
*
|
|
* Since: 4.16
|
|
**/
|
|
gboolean
|
|
gsk_render_node_get_opaque_rect (GskRenderNode *self,
|
|
graphene_rect_t *out_opaque)
|
|
{
|
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (self), FALSE);
|
|
g_return_val_if_fail (out_opaque != NULL, FALSE);
|
|
|
|
if (self->fully_opaque)
|
|
{
|
|
*out_opaque = self->bounds;
|
|
return TRUE;
|
|
}
|
|
|
|
return GSK_RENDER_NODE_GET_CLASS (self)->get_opaque_rect (self, out_opaque);
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_write_to_file:
|
|
* @node: a `GskRenderNode`
|
|
* @filename: (type filename): the file to save it to.
|
|
* @error: Return location for a potential error
|
|
*
|
|
* This function is equivalent to calling [method@Gsk.RenderNode.serialize]
|
|
* followed by [func@GLib.file_set_contents].
|
|
*
|
|
* See those two functions for details on the arguments.
|
|
*
|
|
* It is mostly intended for use inside a debugger to quickly dump a render
|
|
* node to a file for later inspection.
|
|
*
|
|
* Returns: %TRUE if saving was successful
|
|
**/
|
|
gboolean
|
|
gsk_render_node_write_to_file (GskRenderNode *node,
|
|
const char *filename,
|
|
GError **error)
|
|
{
|
|
GBytes *bytes;
|
|
gboolean result;
|
|
|
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), FALSE);
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
bytes = gsk_render_node_serialize (node);
|
|
result = g_file_set_contents (filename,
|
|
g_bytes_get_data (bytes, NULL),
|
|
g_bytes_get_size (bytes),
|
|
error);
|
|
g_bytes_unref (bytes);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gsk_render_node_deserialize:
|
|
* @bytes: the bytes containing the data
|
|
* @error_func: (nullable) (scope call) (closure user_data): Callback on parsing errors
|
|
* @user_data: user_data for @error_func
|
|
*
|
|
* Loads data previously created via [method@Gsk.RenderNode.serialize].
|
|
*
|
|
* For a discussion of the supported format, see that function.
|
|
*
|
|
* Returns: (nullable) (transfer full): a new `GskRenderNode`
|
|
*/
|
|
GskRenderNode *
|
|
gsk_render_node_deserialize (GBytes *bytes,
|
|
GskParseErrorFunc error_func,
|
|
gpointer user_data)
|
|
{
|
|
GskRenderNode *node = NULL;
|
|
|
|
node = gsk_render_node_deserialize_from_bytes (bytes, error_func, user_data);
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* gsk_value_set_render_node:
|
|
* @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
|
|
* @node: a `GskRenderNode`
|
|
*
|
|
* Stores the given `GskRenderNode` inside `value`.
|
|
*
|
|
* The [struct@GObject.Value] will acquire a reference to the `node`.
|
|
*
|
|
* Since: 4.6
|
|
*/
|
|
void
|
|
gsk_value_set_render_node (GValue *value,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *old_node;
|
|
|
|
g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
|
|
|
|
old_node = value->data[0].v_pointer;
|
|
|
|
if (node != NULL)
|
|
{
|
|
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
|
|
|
value->data[0].v_pointer = gsk_render_node_ref (node);
|
|
}
|
|
else
|
|
{
|
|
value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
if (old_node != NULL)
|
|
gsk_render_node_unref (old_node);
|
|
}
|
|
|
|
/**
|
|
* gsk_value_take_render_node:
|
|
* @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
|
|
* @node: (transfer full) (nullable): a `GskRenderNode`
|
|
*
|
|
* Stores the given `GskRenderNode` inside `value`.
|
|
*
|
|
* This function transfers the ownership of the `node` to the `GValue`.
|
|
*
|
|
* Since: 4.6
|
|
*/
|
|
void
|
|
gsk_value_take_render_node (GValue *value,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *old_node;
|
|
|
|
g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
|
|
|
|
old_node = value->data[0].v_pointer;
|
|
|
|
if (node != NULL)
|
|
{
|
|
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
|
|
|
value->data[0].v_pointer = node;
|
|
}
|
|
else
|
|
{
|
|
value->data[0].v_pointer = NULL;
|
|
}
|
|
|
|
if (old_node != NULL)
|
|
gsk_render_node_unref (old_node);
|
|
}
|
|
|
|
/**
|
|
* gsk_value_get_render_node:
|
|
* @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
|
|
*
|
|
* Retrieves the `GskRenderNode` stored inside the given `value`.
|
|
*
|
|
* Returns: (transfer none) (nullable): a `GskRenderNode`
|
|
*
|
|
* Since: 4.6
|
|
*/
|
|
GskRenderNode *
|
|
gsk_value_get_render_node (const GValue *value)
|
|
{
|
|
g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
|
|
|
|
return value->data[0].v_pointer;
|
|
}
|
|
|
|
/**
|
|
* gsk_value_dup_render_node:
|
|
* @value: a [struct@GObject.Value] initialized with type `GSK_TYPE_RENDER_NODE`
|
|
*
|
|
* Retrieves the `GskRenderNode` stored inside the given `value`, and acquires
|
|
* a reference to it.
|
|
*
|
|
* Returns: (transfer full) (nullable): a `GskRenderNode`
|
|
*
|
|
* Since: 4.6
|
|
*/
|
|
GskRenderNode *
|
|
gsk_value_dup_render_node (const GValue *value)
|
|
{
|
|
g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
|
|
|
|
if (value->data[0].v_pointer == NULL)
|
|
return NULL;
|
|
|
|
return gsk_render_node_ref (value->data[0].v_pointer);
|
|
}
|
|
|
|
GdkMemoryDepth
|
|
gsk_render_node_get_preferred_depth (const GskRenderNode *node)
|
|
{
|
|
return node->preferred_depth;
|
|
}
|
|
|
|
gboolean
|
|
gsk_render_node_is_hdr (const GskRenderNode *node)
|
|
{
|
|
return node->is_hdr;
|
|
}
|
|
|
|
/* Whether we need an offscreen to handle opacity correctly for this node.
|
|
* We don't if there is only one drawing node inside (could be child
|
|
* node, or grandchild, or...).
|
|
*
|
|
* For containers with multiple children, we can avoid the offscreen if
|
|
* the children are known not to overlap.
|
|
*/
|
|
gboolean
|
|
gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node)
|
|
{
|
|
return node->offscreen_for_opacity;
|
|
}
|