/* * Copyright (c) 2016 Red Hat, Inc. * * 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 . */ #include "config.h" #include "rendernodeview.h" #include #include #include #include #include "gsk/gskrendernodeprivate.h" #include "fallback-c89.c" typedef struct _GtkRenderNodeViewPrivate GtkRenderNodeViewPrivate; struct _GtkRenderNodeViewPrivate { GdkRectangle viewport; GskRenderNode *render_node; cairo_region_t *render_region; cairo_region_t *clip_region; }; enum { PROP_0, PROP_VIEWPORT, PROP_RENDER_NODE, PROP_RENDER_REGION, PROP_CLIP_REGION, LAST_PROP }; static GParamSpec *props[LAST_PROP] = { NULL, }; G_DEFINE_TYPE_WITH_PRIVATE (GtkRenderNodeView, gtk_render_node_view, GTK_TYPE_WIDGET) static gboolean gtk_render_node_view_has_viewport (GtkRenderNodeView *view) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); return priv->viewport.width > 0 && priv->viewport.height > 0; } static void gtk_render_node_view_get_effective_viewport (GtkRenderNodeView *view, GdkRectangle *viewport) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); if (gtk_render_node_view_has_viewport (view)) { *viewport = priv->viewport; } else if (priv->render_node != NULL) { graphene_rect_t bounds; gsk_render_node_get_bounds (priv->render_node, &bounds); viewport->x = bounds.origin.x; viewport->y = bounds.origin.y; viewport->width = bounds.size.width; viewport->height = bounds.size.height; } else { *viewport = (GdkRectangle) { 0, 0, 0, 0 }; } } static void gtk_render_node_view_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GtkRenderNodeView *view = GTK_RENDER_NODE_VIEW (object); GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); switch (param_id) { case PROP_VIEWPORT: if (gtk_render_node_view_has_viewport (view)) g_value_set_boxed (value, &priv->viewport); else g_value_set_boxed (value, NULL); break; case PROP_RENDER_NODE: g_value_set_pointer (value, priv->render_node); break; case PROP_RENDER_REGION: g_value_set_boxed (value, priv->render_region); break; case PROP_CLIP_REGION: g_value_set_boxed (value, priv->clip_region); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void gtk_render_node_view_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GtkRenderNodeView *view = GTK_RENDER_NODE_VIEW (object); switch (param_id) { case PROP_VIEWPORT: gtk_render_node_view_set_viewport (view, g_value_get_boxed (value)); break; case PROP_RENDER_NODE: gtk_render_node_view_set_render_node (view, g_value_get_pointer (value)); break; case PROP_RENDER_REGION: gtk_render_node_view_set_render_region (view, g_value_get_boxed (value)); break; case PROP_CLIP_REGION: gtk_render_node_view_set_clip_region (view, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void gtk_render_node_view_dispose (GObject *object) { GtkRenderNodeView *view = GTK_RENDER_NODE_VIEW (object); GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); g_clear_pointer (&priv->render_node, gsk_render_node_unref); g_clear_pointer (&priv->render_region, cairo_region_destroy); g_clear_pointer (&priv->clip_region, cairo_region_destroy); G_OBJECT_CLASS (gtk_render_node_view_parent_class)->dispose (object); } static void gtk_render_node_view_measure (GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline) { GtkRenderNodeView *view = GTK_RENDER_NODE_VIEW (widget); GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); GdkRectangle viewport; *minimum = 1; if (priv->render_node == NULL) { *natural = *minimum; return; } gtk_render_node_view_get_effective_viewport (view, &viewport); if (for_size < 0) { if (orientation == GTK_ORIENTATION_HORIZONTAL) *natural = viewport.width; else *natural = viewport.height; } else { if (orientation == GTK_ORIENTATION_HORIZONTAL) { if (for_size > viewport.height) *natural = viewport.width; else *natural = ceil ((double) for_size * viewport.width / viewport.height); } else { if (for_size > viewport.width) *natural = viewport.height; else *natural = ceil ((double) for_size * viewport.height / viewport.width); } } } static void gtk_render_node_view_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { GtkRenderNodeView *view = GTK_RENDER_NODE_VIEW (widget); GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); GdkRectangle viewport; graphene_rect_t rect; int width, height; cairo_t *cr; if (priv->render_node == NULL) return; gtk_render_node_view_get_effective_viewport (view, &viewport); width = gtk_widget_get_width (widget); height = gtk_widget_get_height (widget); graphene_rect_init (&rect, 0, 0, width, height); cr = gtk_snapshot_append_cairo (snapshot, &rect, "RenderNodeView"); cairo_translate (cr, width / 2.0, height / 2.0); if (width < viewport.width || height < viewport.height) { double scale = MIN ((double) width / viewport.width, (double) height / viewport.height); cairo_scale (cr, scale, scale); } cairo_translate (cr, - viewport.x - viewport.width / 2.0, - viewport.y - viewport.height / 2.0); gsk_render_node_draw (priv->render_node, cr); if (priv->render_region) { cairo_region_t *draw; cairo_pattern_t *linear; linear = cairo_pattern_create_linear (0, 0, 10, 10); cairo_pattern_set_extend (linear, CAIRO_EXTEND_REPEAT); cairo_pattern_add_color_stop_rgba (linear, 0.4, 0, 0, 0, 0); cairo_pattern_add_color_stop_rgba (linear, 0.45, 0, 0, 0, 0.5); cairo_pattern_add_color_stop_rgba (linear, 0.55, 0, 0, 0, 0.5); cairo_pattern_add_color_stop_rgba (linear, 0.6, 0, 0, 0, 0); draw = cairo_region_create_rectangle (&viewport); cairo_region_subtract (draw, priv->render_region); cairo_set_source (cr, linear); gdk_cairo_region (cr, draw); cairo_fill (cr); cairo_region_destroy (draw); cairo_pattern_destroy (linear); } if (priv->clip_region) { cairo_region_t *draw; draw = cairo_region_create_rectangle (&viewport); cairo_region_subtract (draw, priv->clip_region); cairo_set_source_rgba (cr, 0, 0, 0, 0.5); gdk_cairo_region (cr, draw); cairo_fill (cr); cairo_region_destroy (draw); } cairo_destroy (cr); } static void gtk_render_node_view_class_init (GtkRenderNodeViewClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = gtk_render_node_view_get_property; object_class->set_property = gtk_render_node_view_set_property; object_class->dispose = gtk_render_node_view_dispose; widget_class->measure = gtk_render_node_view_measure; widget_class->snapshot = gtk_render_node_view_snapshot; props[PROP_VIEWPORT] = g_param_spec_boxed ("viewport", "Viewport", "Viewport", CAIRO_GOBJECT_TYPE_RECTANGLE_INT, G_PARAM_READWRITE); props[PROP_RENDER_NODE] = g_param_spec_pointer ("render-node", "Render node", "Render node", /* GSK_TYPE_RENDER_NODE, */ G_PARAM_READWRITE); props[PROP_RENDER_REGION] = g_param_spec_boxed ("render-region", "Render region", "Actually rendered region", CAIRO_GOBJECT_TYPE_REGION, G_PARAM_READWRITE); props[PROP_CLIP_REGION] = g_param_spec_boxed ("clip-region", "Clip region", "Clip region", CAIRO_GOBJECT_TYPE_REGION, G_PARAM_READWRITE); g_object_class_install_properties (object_class, LAST_PROP, props); } static void gtk_render_node_view_init (GtkRenderNodeView *view) { gtk_widget_set_has_window (GTK_WIDGET (view), FALSE); } GtkWidget * gtk_render_node_view_new (void) { return g_object_new (GTK_TYPE_RENDER_NODE_VIEW, NULL); } void gtk_render_node_view_set_render_node (GtkRenderNodeView *view, GskRenderNode *node) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); if (priv->render_node == node) return; g_clear_pointer (&priv->render_node, gsk_render_node_unref); if (node) priv->render_node = gsk_render_node_ref (node); if (gtk_render_node_view_has_viewport (view)) gtk_widget_queue_draw (GTK_WIDGET (view)); else gtk_widget_queue_resize (GTK_WIDGET (view)); g_object_notify_by_pspec (G_OBJECT (view), props[PROP_RENDER_NODE]); } GskRenderNode * gtk_render_node_view_get_render_node (GtkRenderNodeView *view) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); return priv->render_node; } void gtk_render_node_view_set_viewport (GtkRenderNodeView *view, const GdkRectangle *viewport) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); if (viewport) { priv->viewport = *viewport; } else { priv->viewport = (GdkRectangle) { 0, 0, 0, 0 }; } gtk_widget_queue_resize (GTK_WIDGET (view)); g_object_notify_by_pspec (G_OBJECT (view), props[PROP_VIEWPORT]); } void gtk_render_node_view_get_viewport (GtkRenderNodeView *view, GdkRectangle *viewport) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); *viewport = priv->viewport; } void gtk_render_node_view_set_clip_region (GtkRenderNodeView *view, const cairo_region_t *clip) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); if (priv->clip_region) cairo_region_destroy (priv->clip_region); priv->clip_region = cairo_region_copy (clip); gtk_widget_queue_draw (GTK_WIDGET (view)); g_object_notify_by_pspec (G_OBJECT (view), props[PROP_CLIP_REGION]); } const cairo_region_t* gtk_render_node_view_get_clip_region (GtkRenderNodeView *view) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); return priv->clip_region; } void gtk_render_node_view_set_render_region (GtkRenderNodeView *view, const cairo_region_t *region) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); if (priv->render_region) cairo_region_destroy (priv->render_region); priv->render_region = cairo_region_copy (region); gtk_widget_queue_draw (GTK_WIDGET (view)); g_object_notify_by_pspec (G_OBJECT (view), props[PROP_RENDER_REGION]); } const cairo_region_t* gtk_render_node_view_get_render_region (GtkRenderNodeView *view) { GtkRenderNodeViewPrivate *priv = gtk_render_node_view_get_instance_private (view); return priv->render_region; } // vim: set et sw=2 ts=2: