[gnome-photos/wip/rishi/double-buffering: 7/12] image-view: Implement double-buffering
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/double-buffering: 7/12] image-view: Implement double-buffering
- Date: Tue, 17 Jan 2017 12:39:15 +0000 (UTC)
commit 22fd2ed2aac158f93753d727e88833b9a303cbe5
Author: Debarshi Ray <debarshir gnome org>
Date: Tue Jan 17 10:37:44 2017 +0100
image-view: Implement double-buffering
There are some drawbacks to directly blitting the GeglNode that was
passed to PhotosImageView.
GEGL processes the graph in smaller chunks. The GeglNode might not be
fully processed when we need to draw the widget, leading to rendering
artifacts. We made this bearable in commit
d2f4589f7747265786aed0da4046235f63f8e8d4 by not queuing a draw after
each chunk is computed. However, the problem was still there. A draw
may be queued for reasons completely beyond control. eg., the widget
can get resized.
Secondly, the zoom and positioning of the image were being updated for
every chunk. It is wasteful. More importantly, due to the chunked
processing, the output of the GeglNode might not be ready to match the
latest value of the bounding box.
Therefore, a separate graph, with a single gegl:buffer-source, is used
for rendering. The output of the GeglNode is copied to a new
GeglBuffer and fed into this graph. Changes to the GeglNode's bounding
box, caused by modifications to its graph's connectivity or
properties, are tracked, and so is the amount of area
that has been computed. We update our GeglBuffer, zoom and positioning
only when the entire bounding box has been computed.
src/photos-image-view.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 127 insertions(+), 3 deletions(-)
---
diff --git a/src/photos-image-view.c b/src/photos-image-view.c
index e7b8363..2629ade 100644
--- a/src/photos-image-view.c
+++ b/src/photos-image-view.c
@@ -21,18 +21,24 @@
#include "config.h"
#include <babl/babl.h>
+#include <cairo.h>
#include <cairo-gobject.h>
#include <glib.h>
#include "photos-debug.h"
#include "photos-image-view.h"
#include "photos-marshalers.h"
+#include "photos-utils.h"
struct _PhotosImageView
{
GtkDrawingArea parent_instance;
+ GeglNode *buffer_source;
+ GeglNode *graph;
GeglNode *node;
+ cairo_region_t *bbox_region;
+ cairo_region_t *region;
gfloat x;
gfloat x_scaled;
gfloat y;
@@ -63,6 +69,63 @@ static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (PhotosImageView, photos_image_view, GTK_TYPE_DRAWING_AREA);
+static void photos_image_view_computed (PhotosImageView *self, GeglRectangle *rect);
+
+
+static void
+photos_image_view_update_graph (PhotosImageView *self)
+{
+ const Babl *format;
+ GeglBuffer *buffer = NULL;
+ gint64 end;
+ gint64 start;
+
+ g_clear_object (&self->graph);
+ g_signal_handlers_block_by_func (self->node, photos_image_view_computed, self);
+
+ format = babl_format ("cairo-ARGB32");
+ buffer = photos_utils_dup_buffer_from_node (self->node, format);
+
+ self->graph = gegl_node_new ();
+ self->buffer_source = gegl_node_new_child (self->graph,
+ "operation", "gegl:buffer-source",
+ "buffer", buffer,
+ NULL);
+
+ start = g_get_monotonic_time ();
+
+ gegl_node_process (self->buffer_source);
+
+ end = g_get_monotonic_time ();
+ photos_debug (PHOTOS_DEBUG_GEGL, "PhotosImageView: Process Buffer: %" G_GINT64_FORMAT, end - start);
+
+ g_signal_handlers_unblock_by_func (self->node, photos_image_view_computed, self);
+ g_object_unref (buffer);
+}
+
+
+static void
+photos_image_view_update_region (PhotosImageView *self)
+{
+ GeglRectangle bbox;
+
+ g_clear_pointer (&self->bbox_region, (GDestroyNotify) cairo_region_destroy);
+ g_clear_pointer (&self->region, (GDestroyNotify) cairo_region_destroy);
+
+ bbox = gegl_node_get_bounding_box (self->node);
+ self->bbox_region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &bbox);
+ self->region = cairo_region_create ();
+
+ photos_debug (PHOTOS_DEBUG_GEGL,
+ "PhotosImageView: Node (%p) Region: %d, %d, %d×%d",
+ self->node,
+ bbox.x,
+ bbox.y,
+ bbox.width,
+ bbox.height);
+}
+
+
static void
photos_image_view_update (PhotosImageView *self)
{
@@ -76,12 +139,14 @@ photos_image_view_update (PhotosImageView *self)
if (self->node == NULL)
return;
+ g_return_if_fail (GEGL_IS_NODE (self->buffer_source));
+
gtk_widget_get_allocation (GTK_WIDGET (self), &viewport);
if (viewport.width < 0 || viewport.height < 0)
return;
- bbox = gegl_node_get_bounding_box (self->node);
+ bbox = gegl_node_get_bounding_box (self->buffer_source);
if (bbox.width < 0 || bbox.height < 0)
return;
@@ -118,6 +183,13 @@ photos_image_view_update (PhotosImageView *self)
static void
photos_image_view_computed (PhotosImageView *self, GeglRectangle *rect)
{
+ cairo_status_t status;
+
+ g_return_if_fail (GEGL_IS_NODE (self->buffer_source));
+ g_return_if_fail (GEGL_IS_NODE (self->graph));
+ g_return_if_fail (self->bbox_region != NULL);
+ g_return_if_fail (self->region != NULL);
+
photos_debug (PHOTOS_DEBUG_GEGL,
"PhotosImageView: Node (%p) Computed: %d, %d, %d×%d",
self->node,
@@ -126,11 +198,31 @@ photos_image_view_computed (PhotosImageView *self, GeglRectangle *rect)
rect->width,
rect->height);
+ status = cairo_region_union_rectangle (self->region, (cairo_rectangle_int_t *) rect);
+ g_return_if_fail (status == CAIRO_STATUS_SUCCESS);
+
+ if (!cairo_region_equal (self->bbox_region, self->region))
+ return;
+
+ photos_debug (PHOTOS_DEBUG_GEGL, "PhotosImageView: Node (%p) Computing Completed", self->node);
+
+ photos_image_view_update_graph (self);
photos_image_view_update (self);
}
static void
+photos_image_view_invalidated (PhotosImageView *self)
+{
+ g_return_if_fail (PHOTOS_IS_IMAGE_VIEW (self));
+ g_return_if_fail (GEGL_IS_NODE (self->node));
+
+ photos_debug (PHOTOS_DEBUG_GEGL, "PhotosImageView: Node (%p) Invalidated", self->node);
+ photos_image_view_update_region (self);
+}
+
+
+static void
photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *rect)
{
const Babl *format;
@@ -142,6 +234,8 @@ photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *r
gint64 end;
gint64 start;
+ g_return_if_fail (GEGL_IS_NODE (self->buffer_source));
+
scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
roi.x = (gint) self->x_scaled + rect->x * scale_factor;
@@ -155,7 +249,7 @@ photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *r
start = g_get_monotonic_time ();
- gegl_node_blit (self->node,
+ gegl_node_blit (self->buffer_source,
(gdouble) self->zoom_scaled,
&roi,
format,
@@ -221,6 +315,7 @@ photos_image_view_dispose (GObject *object)
{
PhotosImageView *self = PHOTOS_IMAGE_VIEW (object);
+ g_clear_object (&self->graph);
g_clear_object (&self->node);
G_OBJECT_CLASS (photos_image_view_parent_class)->dispose (object);
@@ -228,6 +323,18 @@ photos_image_view_dispose (GObject *object)
static void
+photos_image_view_finalize (GObject *object)
+{
+ PhotosImageView *self = PHOTOS_IMAGE_VIEW (object);
+
+ g_clear_pointer (&self->bbox_region, (GDestroyNotify) cairo_region_destroy);
+ g_clear_pointer (&self->region, (GDestroyNotify) cairo_region_destroy);
+
+ G_OBJECT_CLASS (photos_image_view_parent_class)->finalize (object);
+}
+
+
+static void
photos_image_view_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
PhotosImageView *self = PHOTOS_IMAGE_VIEW (object);
@@ -303,6 +410,7 @@ photos_image_view_class_init (PhotosImageViewClass *class)
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = photos_image_view_dispose;
+ object_class->finalize = photos_image_view_finalize;
object_class->get_property = photos_image_view_get_property;
object_class->set_property = photos_image_view_set_property;
widget_class->draw = photos_image_view_draw;
@@ -428,14 +536,30 @@ photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
return;
if (self->node != NULL)
- g_signal_handlers_disconnect_by_func (self->node, photos_image_view_computed, self);
+ {
+ g_signal_handlers_disconnect_by_func (self->node, photos_image_view_computed, self);
+ g_signal_handlers_disconnect_by_func (self->node, photos_image_view_invalidated, self);
+ }
+ self->buffer_source = NULL;
+ g_clear_object (&self->graph);
g_clear_object (&self->node);
+ g_clear_pointer (&self->bbox_region, (GDestroyNotify) cairo_region_destroy);
+ g_clear_pointer (&self->region, (GDestroyNotify) cairo_region_destroy);
if (node != NULL)
{
self->node = g_object_ref (node);
+
+ photos_image_view_update_region (self);
+ photos_image_view_update_graph (self);
+
g_signal_connect_object (node, "computed", G_CALLBACK (photos_image_view_computed), self,
G_CONNECT_SWAPPED);
+ g_signal_connect_object (node,
+ "invalidated",
+ G_CALLBACK (photos_image_view_invalidated),
+ self,
+ G_CONNECT_SWAPPED);
}
photos_image_view_update (self);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]