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])