diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index d0b5413a73..3756b5b4e3 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -75,6 +75,8 @@ libappdisplay_a_sources = \
gimpdisplay-foreach.h \
gimpdisplay-handlers.c \
gimpdisplay-handlers.h \
+ gimpdisplay-transport.c \
+ gimpdisplay-transport.h \
gimpdisplayshell.c \
gimpdisplayshell.h \
gimpdisplayshell-appearance.c \
diff --git a/app/display/gimpdisplay-transport.c b/app/display/gimpdisplay-transport.c
new file mode 100644
index 0000000000..b2808a1c78
--- /dev/null
+++ b/app/display/gimpdisplay-transport.c
@@ -0,0 +1,234 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "gimpdisplay-transport.h"
+
+#define NUM_PAGES 2
+
+struct GimpDisplayXfer {
+ struct rtree {
+ struct rtree_node {
+ struct rtree_node *children[2];
+ struct rtree_node *next;
+ int x, y, w, h;
+ } root, *available;
+ } rtree; /* track subregions of render_surface for efficient uploads */
+ cairo_surface_t *render_surface[NUM_PAGES];
+ int page;
+};
+
+static struct rtree_node *
+rtree_node_create (struct rtree *rtree, struct rtree_node **prev,
+ int x, int y, int w, int h)
+{
+ struct rtree_node *node;
+
+ g_assert(x >= 0 && x+w <= rtree->root.w);
+ g_assert(y >= 0 && y+h <= rtree->root.h);
+
+ node = g_slice_alloc (sizeof (*node));
+ if (node == NULL)
+ return NULL;
+
+ node->children[0] = NULL;
+ node->children[1] = NULL;
+ node->x = x;
+ node->y = y;
+ node->w = w;
+ node->h = h;
+
+ node->next = *prev;
+ *prev = node;
+
+ return node;
+}
+
+static void
+rtree_node_destroy (struct rtree *rtree, struct rtree_node *node)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (node->children[i])
+ rtree_node_destroy (rtree, node->children[i]);
+ }
+
+ g_slice_free (struct rtree_node, node);
+}
+
+static struct rtree_node *
+rtree_node_insert (struct rtree *rtree, struct rtree_node **prev,
+ struct rtree_node *node, int w, int h)
+{
+ *prev = node->next;
+
+ if (((node->w - w) | (node->h - h)) > 1)
+ {
+ int ww = node->w - w;
+ int hh = node->h - h;
+
+ if (ww >= hh)
+ {
+ node->children[0] = rtree_node_create (rtree, prev,
+ node->x + w, node->y,
+ ww, node->h);
+ node->children[1] = rtree_node_create (rtree, prev,
+ node->x, node->y + h,
+ w, hh);
+ }
+ else
+ {
+ node->children[0] = rtree_node_create (rtree, prev,
+ node->x, node->y + h,
+ node->w, hh);
+ node->children[1] = rtree_node_create (rtree, prev,
+ node->x + w, node->y,
+ ww, h);
+ }
+ }
+
+ return node;
+}
+
+static struct rtree_node *
+rtree_insert (struct rtree *rtree, int w, int h)
+{
+ struct rtree_node *node, **prev;
+
+ for (prev = &rtree->available; (node = *prev); prev = &node->next)
+ if (node->w >= w && w < 2 * node->w && node->h >= h && h < 2 * node->h)
+ return rtree_node_insert (rtree, prev, node, w, h);
+
+ for (prev = &rtree->available; (node = *prev); prev = &node->next)
+ if (node->w >= w && node->h >= h)
+ return rtree_node_insert (rtree, prev, node, w, h);
+
+ return NULL;
+}
+
+static void
+rtree_init (struct rtree *rtree, int w, int h)
+{
+ rtree->root.x = 0;
+ rtree->root.y = 0;
+ rtree->root.w = w;
+ rtree->root.h = h;
+ rtree->root.children[0] = NULL;
+ rtree->root.children[1] = NULL;
+ rtree->root.next = NULL;
+ rtree->available = &rtree->root;
+}
+
+static void
+rtree_reset (struct rtree *rtree)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (rtree->root.children[i] == NULL)
+ continue;
+
+ rtree_node_destroy (rtree, rtree->root.children[i]);
+ rtree->root.children[i] = NULL;
+ }
+
+ rtree->root.next = NULL;
+ rtree->available = &rtree->root;
+}
+
+static void
+xfer_destroy (void *data)
+{
+ GimpDisplayXfer *xfer = data;
+ gint i;
+
+ for (i = 0; i < NUM_PAGES; i++)
+ cairo_surface_destroy (xfer->render_surface[i]);
+
+ rtree_reset (&xfer->rtree);
+ g_free (xfer);
+}
+
+GimpDisplayXfer *
+gimp_display_xfer_realize (GtkWidget *widget)
+{
+ GdkScreen *screen;
+ GimpDisplayXfer *xfer;
+
+ screen = gtk_widget_get_screen (widget);
+ xfer = g_object_get_data (G_OBJECT (screen), "gimpdisplay-transport");
+ if (xfer == NULL)
+ {
+ cairo_t *cr;
+ gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH * GIMP_DISPLAY_RENDER_MAX_SCALE;
+ gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT * GIMP_DISPLAY_RENDER_MAX_SCALE;
+ int n;
+
+ xfer = g_new (GimpDisplayXfer, 1);
+ rtree_init (&xfer->rtree, w, h);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ for (n = 0; n < NUM_PAGES; n++)
+ {
+ xfer->render_surface[n] =
+ cairo_surface_create_similar_image (cairo_get_target (cr),
+ CAIRO_FORMAT_ARGB32, w, h);
+ cairo_surface_mark_dirty (xfer->render_surface[n]);
+ }
+ cairo_destroy (cr);
+ xfer->page = 0;
+
+ g_object_set_data_full (G_OBJECT (screen),
+ "gimpdisplay-transport",
+ xfer, xfer_destroy);
+ }
+
+ return xfer;
+}
+
+cairo_surface_t *
+gimp_display_xfer_get_surface (GimpDisplayXfer *xfer,
+ gint w, gint h,
+ gint *src_x, gint *src_y)
+{
+ struct rtree_node *node;
+
+ g_assert (w <= GIMP_DISPLAY_RENDER_BUF_WIDTH * GIMP_DISPLAY_RENDER_MAX_SCALE &&
+ h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT * GIMP_DISPLAY_RENDER_MAX_SCALE);
+
+ node = rtree_insert (&xfer->rtree, w, h);
+ if (node == NULL)
+ {
+ xfer->page = (xfer->page + 1) % NUM_PAGES;
+ cairo_surface_flush (xfer->render_surface[xfer->page]);
+ rtree_reset (&xfer->rtree);
+ cairo_surface_mark_dirty (xfer->render_surface[xfer->page]); /* XXX */
+ node = rtree_insert (&xfer->rtree, w, h);
+ g_assert (node != NULL);
+ }
+
+ *src_x = node->x;
+ *src_y = node->y;
+ return xfer->render_surface[xfer->page];
+}
diff --git a/app/display/gimpdisplay-transport.h b/app/display/gimpdisplay-transport.h
new file mode 100644
index 0000000000..6c7b21f967
--- /dev/null
+++ b/app/display/gimpdisplay-transport.h
@@ -0,0 +1,44 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __GIMP_DISPLAY_TRANSPORT_H__
+#define __GIMP_DISPLAY_TRANSPORT_H__
+
+#include
+
+/* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */
+
+#define GIMP_DISPLAY_RENDER_BUF_WIDTH 256
+#define GIMP_DISPLAY_RENDER_BUF_HEIGHT 256
+
+#ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING
+#define GIMP_DISPLAY_RENDER_MAX_SCALE 2.0
+#else
+#define GIMP_DISPLAY_RENDER_MAX_SCALE 1.0
+#endif
+
+typedef struct GimpDisplayXfer GimpDisplayXfer;
+
+GimpDisplayXfer *
+gimp_display_xfer_realize (GtkWidget *widget);
+
+cairo_surface_t *
+gimp_display_xfer_get_surface (GimpDisplayXfer *xfer,
+ gint w, gint h,
+ gint *src_x, gint *src_y);
+
+#endif /* __GIMP_DISPLAY_TRANSPORT_H__ */
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index e8cf44daa4..8a5593259b 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -109,6 +109,8 @@ gimp_display_shell_canvas_realize (GtkWidget *canvas,
/* allow shrinking */
gtk_widget_set_size_request (GTK_WIDGET (shell), 0, 0);
+
+ shell->xfer = gimp_display_xfer_realize (GTK_WIDGET(shell));
}
void
diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c
index 67d1c50d25..ed4024be8c 100644
--- a/app/display/gimpdisplayshell-render.c
+++ b/app/display/gimpdisplayshell-render.c
@@ -42,7 +42,6 @@
#include "gimpdisplayshell-render.h"
#include "gimpdisplayshell-scroll.h"
-
void
gimp_display_shell_render (GimpDisplayShell *shell,
cairo_t *cr,
@@ -59,6 +58,10 @@ gimp_display_shell_render (GimpDisplayShell *shell,
gint viewport_offset_y;
gint viewport_width;
gint viewport_height;
+ cairo_surface_t *xfer;
+ gint src_x, src_y;
+ gint stride;
+ unsigned char *data;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (cr != NULL);
@@ -80,6 +83,14 @@ gimp_display_shell_render (GimpDisplayShell *shell,
&viewport_offset_y,
&viewport_width,
&viewport_height);
+ xfer = gimp_display_xfer_get_surface (shell->xfer,
+ w * window_scale,
+ h * window_scale,
+ &src_x, &src_y);
+
+ stride =cairo_image_surface_get_stride (xfer);
+ data = cairo_image_surface_get_data (xfer);
+ data += src_y * stride + src_x * 4;
gegl_buffer_get (buffer,
GEGL_RECTANGLE ((x + viewport_offset_x) * window_scale,
@@ -88,33 +99,21 @@ gimp_display_shell_render (GimpDisplayShell *shell,
h * window_scale),
shell->scale_x * window_scale,
babl_format ("cairo-ARGB32"),
- cairo_image_surface_get_data (shell->render_surface),
- cairo_image_surface_get_stride (shell->render_surface),
+ data, stride,
GEGL_ABYSS_NONE);
/* apply filters to the rendered projection */
if (shell->filter_stack)
{
- cairo_surface_t *sub = shell->render_surface;
-
- if (w != GIMP_DISPLAY_RENDER_BUF_WIDTH ||
- h != GIMP_DISPLAY_RENDER_BUF_HEIGHT)
- sub = cairo_image_surface_create_for_data (cairo_image_surface_get_data (sub),
- CAIRO_FORMAT_ARGB32,
- w * window_scale,
- h * window_scale,
- GIMP_DISPLAY_RENDER_BUF_WIDTH * 4);
-
- gimp_color_display_stack_convert_surface (shell->filter_stack, sub);
-
- if (sub != shell->render_surface)
- cairo_surface_destroy (sub);
+ cairo_surface_t *image =
+ cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
+ w * window_scale,
+ h * window_scale,
+ stride);
+ gimp_color_display_stack_convert_surface (shell->filter_stack, image);
+ cairo_surface_destroy (image);
}
- cairo_surface_mark_dirty_rectangle (shell->render_surface,
- 0, 0,
- w * window_scale, h * window_scale);
-
#if 0
if (shell->mask)
{
@@ -151,9 +150,9 @@ gimp_display_shell_render (GimpDisplayShell *shell,
cairo_scale (cr, 1.0 / window_scale, 1.0 / window_scale);
- cairo_set_source_surface (cr, shell->render_surface,
- x * window_scale,
- y * window_scale);
+ cairo_set_source_surface (cr, xfer,
+ (x - src_x) * window_scale,
+ (y - src_y) * window_scale);
cairo_paint (cr);
diff --git a/app/display/gimpdisplayshell-render.h b/app/display/gimpdisplayshell-render.h
index 652cddd6e1..a84bc218cc 100644
--- a/app/display/gimpdisplayshell-render.h
+++ b/app/display/gimpdisplayshell-render.h
@@ -18,19 +18,6 @@
#ifndef __GIMP_DISPLAY_SHELL_RENDER_H__
#define __GIMP_DISPLAY_SHELL_RENDER_H__
-
-/* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */
-
-#define GIMP_DISPLAY_RENDER_BUF_WIDTH 256
-#define GIMP_DISPLAY_RENDER_BUF_HEIGHT 256
-
-#ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING
-#define GIMP_DISPLAY_RENDER_MAX_SCALE 2.0
-#else
-#define GIMP_DISPLAY_RENDER_MAX_SCALE 1.0
-#endif
-
-
void gimp_display_shell_render (GimpDisplayShell *shell,
cairo_t *cr,
gint x,
@@ -38,5 +25,4 @@ void gimp_display_shell_render (GimpDisplayShell *shell,
gint w,
gint h);
-
#endif /* __GIMP_DISPLAY_SHELL_RENDER_H__ */
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index 3e9979963d..5d17c7c8a0 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -299,12 +299,6 @@ gimp_display_shell_init (GimpDisplayShell *shell)
shell->x_src_dec = 1;
shell->y_src_dec = 1;
- shell->render_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
- GIMP_DISPLAY_RENDER_BUF_WIDTH *
- GIMP_DISPLAY_RENDER_MAX_SCALE,
- GIMP_DISPLAY_RENDER_BUF_HEIGHT *
- GIMP_DISPLAY_RENDER_MAX_SCALE);
-
gimp_display_shell_items_init (shell);
shell->icon_size = 32;
@@ -783,12 +777,6 @@ gimp_display_shell_dispose (GObject *object)
shell->filter_idle_id = 0;
}
- if (shell->render_surface)
- {
- cairo_surface_destroy (shell->render_surface);
- shell->render_surface = NULL;
- }
-
if (shell->mask_surface)
{
cairo_surface_destroy (shell->mask_surface);
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index af825f889a..f3d68a504e 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -18,6 +18,7 @@
#ifndef __GIMP_DISPLAY_SHELL_H__
#define __GIMP_DISPLAY_SHELL_H__
+#include "gimpdisplay-transport.h"
/* Apply to a float the same rounding mode used in the renderer */
#define PROJ_ROUND(coord) ((gint) RINT (coord))
@@ -114,7 +115,7 @@ struct _GimpDisplayShell
GtkWidget *statusbar; /* statusbar */
- cairo_surface_t *render_surface; /* buffer for rendering the image */
+ GimpDisplayXfer *xfer;
cairo_surface_t *mask_surface; /* buffer for rendering the mask */
cairo_pattern_t *checkerboard; /* checkerboard pattern */
diff --git a/app/sanity.c b/app/sanity.c
index df14812ca1..98fed6c069 100644
--- a/app/sanity.c
+++ b/app/sanity.c
@@ -163,7 +163,7 @@ static gchar *
sanity_check_cairo (void)
{
#define CAIRO_REQUIRED_MAJOR 1
-#define CAIRO_REQUIRED_MINOR 10
+#define CAIRO_REQUIRED_MINOR 12
#define CAIRO_REQUIRED_MICRO 2
if (cairo_version () < CAIRO_VERSION_ENCODE (CAIRO_REQUIRED_MAJOR,
diff --git a/configure.ac b/configure.ac
index 4792277f59..dd82502360 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,8 +46,8 @@ m4_define([glib_required_version], [2.32.0])
m4_define([atk_required_version], [2.2.0])
m4_define([gtk_required_version], [2.24.10])
m4_define([gdk_pixbuf_required_version], [2.24.1])
-m4_define([cairo_required_version], [1.10.2])
-m4_define([cairo_pdf_required_version], [1.10.2])
+m4_define([cairo_required_version], [1.12.2])
+m4_define([cairo_pdf_required_version], [1.12.2])
m4_define([pangocairo_required_version], [1.29.4])
m4_define([fontconfig_required_version], [2.2.0])
m4_define([gtkdoc_required_version], [1.0])