From 9aa6aa1f040e6476a1e047b09d6c0403cdb8f344 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Mon, 15 Jul 2019 23:24:35 +0200 Subject: [PATCH] app: make display update much faster again Introduce a render cache that keeps the result of scaling, color management, display filters and shell mask (for tools like fuzzy select). Change gimpdisplayshell-render.[ch] to only render to the cache and manage a cairo region of the cache's valid area. Call cache invalidation functions form various places. Change the API of all render functions to be in display coordinates. Also get rid of gimpdisplayxfer.[ch] because we now have a canvas-sized cairo surface which is a surface similar to the destination surface. --- app/display/Makefile.am | 2 - app/display/gimpdisplay.c | 3 + app/display/gimpdisplayshell-callbacks.c | 34 ++- app/display/gimpdisplayshell-draw.c | 51 ++-- app/display/gimpdisplayshell-filter.c | 1 + app/display/gimpdisplayshell-handlers.c | 4 + app/display/gimpdisplayshell-profile.c | 5 +- app/display/gimpdisplayshell-render.c | 260 +++++++++++++++----- app/display/gimpdisplayshell-render.h | 39 ++- app/display/gimpdisplayshell-rotate.c | 3 + app/display/gimpdisplayshell-scale.c | 2 + app/display/gimpdisplayshell-scroll.c | 41 ++++ app/display/gimpdisplayshell.c | 12 +- app/display/gimpdisplayshell.h | 8 +- app/display/gimpdisplayxfer.c | 300 ----------------------- app/display/gimpdisplayxfer.h | 37 --- 16 files changed, 346 insertions(+), 456 deletions(-) delete mode 100644 app/display/gimpdisplayxfer.c delete mode 100644 app/display/gimpdisplayxfer.h diff --git a/app/display/Makefile.am b/app/display/Makefile.am index eda865265d..4429f84478 100644 --- a/app/display/Makefile.am +++ b/app/display/Makefile.am @@ -156,8 +156,6 @@ libappdisplay_a_sources = \ gimpdisplayshell-transform.h \ gimpdisplayshell-utils.c \ gimpdisplayshell-utils.h \ - gimpdisplayxfer.c \ - gimpdisplayxfer.h \ gimpimagewindow.c \ gimpimagewindow.h \ gimpmotionbuffer.c \ diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c index 709f95f09a..1c2914c343 100644 --- a/app/display/gimpdisplay.c +++ b/app/display/gimpdisplay.c @@ -44,6 +44,7 @@ #include "gimpdisplayshell-expose.h" #include "gimpdisplayshell-handlers.h" #include "gimpdisplayshell-icon.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-transform.h" #include "gimpimagewindow.h" @@ -897,4 +898,6 @@ gimp_display_paint_area (GimpDisplay *display, y2 = ceil ((gdouble) y2 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT; gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1); + + gimp_display_shell_render_invalidate_area (shell, x1, y1, x2 - x1, y2 - y1); } diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c index e567563c6a..7b2e281a29 100644 --- a/app/display/gimpdisplayshell-callbacks.c +++ b/app/display/gimpdisplayshell-callbacks.c @@ -38,12 +38,12 @@ #include "gimpdisplayshell-appearance.h" #include "gimpdisplayshell-callbacks.h" #include "gimpdisplayshell-draw.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-scale.h" #include "gimpdisplayshell-scroll.h" #include "gimpdisplayshell-scrollbars.h" #include "gimpdisplayshell-selection.h" #include "gimpdisplayshell-title.h" -#include "gimpdisplayxfer.h" #include "gimpimagewindow.h" #include "gimpnavigationeditor.h" @@ -80,9 +80,10 @@ void gimp_display_shell_canvas_realize (GtkWidget *canvas, GimpDisplayShell *shell) { - GimpCanvasPaddingMode padding_mode; - GimpRGB padding_color; - GtkAllocation allocation; + GimpCanvasPaddingMode padding_mode; + GimpRGB padding_color; + GtkAllocation allocation; + const gchar *env; gtk_widget_grab_focus (canvas); @@ -115,7 +116,27 @@ 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)); + shell->render_buf_width = 256; + shell->render_buf_height = 256; + + env = g_getenv ("GIMP_DISPLAY_RENDER_BUF_SIZE"); + + if (env) + { + gint width = atoi (env); + gint height = width; + + env = strchr (env, 'x'); + if (env) + height = atoi (env + 1); + + if (width > 0 && width <= 8192 && + height > 0 && height <= 8192) + { + shell->render_buf_width = width; + shell->render_buf_height = height; + } + } } static gboolean @@ -130,6 +151,9 @@ gimp_display_shell_canvas_tick (GtkWidget *widget, if ((shell->disp_width != allocation.width) || (shell->disp_height != allocation.height)) { + g_clear_pointer (&shell->render_cache, cairo_surface_destroy); + gimp_display_shell_render_invalidate_full (shell); + if (shell->zoom_on_resize && shell->disp_width > 64 && shell->disp_height > 64 && diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 654828016c..ccc14005eb 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -32,17 +32,10 @@ #include "gimpcanvas.h" #include "gimpcanvas-style.h" -#include "gimpcanvaspath.h" #include "gimpdisplay.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-draw.h" #include "gimpdisplayshell-render.h" -#include "gimpdisplayshell-scale.h" -#include "gimpdisplayshell-transform.h" -#include "gimpdisplayxfer.h" - - -#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 /* public functions */ @@ -157,8 +150,8 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell, * chunk size as necessary, to accommodate for the display * transform and window scale factor. */ - chunk_width = GIMP_DISPLAY_RENDER_BUF_WIDTH; - chunk_height = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + chunk_width = shell->render_buf_width; + chunk_height = shell->render_buf_height; #ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING /* multiply the image scale-factor by the window scale-factor, and divide @@ -197,23 +190,8 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell, for (c = 0; c < n_cols; c++) { - gint x1 = x + (2 * c * w + n_cols) / (2 * n_cols); - gint x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols); - gdouble ix1, iy1; - gdouble ix2, iy2; - gint ix, iy; - gint iw, ih; - - /* map chunk from screen space to scaled image space */ - gimp_display_shell_untransform_bounds_with_scale ( - shell, scale, - x1, y1, x2, y2, - &ix1, &iy1, &ix2, &iy2); - - ix = floor (ix1); - iy = floor (iy1); - iw = ceil (ix2) - ix; - ih = ceil (iy2) - iy; + gint x1 = x + (2 * c * w + n_cols) / (2 * n_cols); + gint x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols); cairo_save (cr); @@ -221,14 +199,21 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell, cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1); cairo_clip (cr); - /* transform to scaled image space, and apply uneven scaling */ - if (shell->rotate_transform) - cairo_transform (cr, shell->rotate_transform); - cairo_translate (cr, -shell->offset_x, -shell->offset_y); - cairo_scale (cr, shell->scale_x / scale, shell->scale_y / scale); + if (! gimp_display_shell_render_is_valid (shell, + x1, y1, x2 - x1, y2 - y1)) + { + /* render image to the render cache */ + gimp_display_shell_render (shell, cr, + x1, y1, x2 - x1, y2 - y1, + scale); - /* render image */ - gimp_display_shell_render (shell, cr, ix, iy, iw, ih, scale); + gimp_display_shell_render_validate_area (shell, + x1, y1, x2 - x1, y2 - y1); + } + + /* render from the render cache to screen */ + cairo_set_source_surface (cr, shell->render_cache, 0, 0); + cairo_paint (cr); cairo_restore (cr); diff --git a/app/display/gimpdisplayshell-filter.c b/app/display/gimpdisplayshell-filter.c index 3992238fb9..ad482820aa 100644 --- a/app/display/gimpdisplayshell-filter.c +++ b/app/display/gimpdisplayshell-filter.c @@ -139,6 +139,7 @@ gimp_display_shell_filter_changed_idle (gpointer data) gimp_display_shell_profile_update (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); shell->filter_idle_id = 0; diff --git a/app/display/gimpdisplayshell-handlers.c b/app/display/gimpdisplayshell-handlers.c index 9d7be6f76a..d3bac52f1c 100644 --- a/app/display/gimpdisplayshell-handlers.c +++ b/app/display/gimpdisplayshell-handlers.c @@ -61,6 +61,7 @@ #include "gimpdisplayshell-handlers.h" #include "gimpdisplayshell-icon.h" #include "gimpdisplayshell-profile.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-rulers.h" #include "gimpdisplayshell-scale.h" #include "gimpdisplayshell-scroll.h" @@ -840,6 +841,7 @@ gimp_display_shell_size_changed_detailed_handler (GimpImage *image, gimp_display_shell_scroll_clamp_and_update (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } } @@ -1048,6 +1050,7 @@ gimp_display_shell_monitor_res_notify_handler (GObject *config, gimp_display_shell_scaled (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } } @@ -1121,6 +1124,7 @@ gimp_display_shell_quality_notify_handler (GObject *config, GimpDisplayShell *shell) { gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } static void diff --git a/app/display/gimpdisplayshell-profile.c b/app/display/gimpdisplayshell-profile.c index 57f290a04a..df6c4b144b 100644 --- a/app/display/gimpdisplayshell-profile.c +++ b/app/display/gimpdisplayshell-profile.c @@ -40,7 +40,6 @@ #include "gimpdisplayshell-actions.h" #include "gimpdisplayshell-filter.h" #include "gimpdisplayshell-profile.h" -#include "gimpdisplayxfer.h" #include "gimp-intl.h" @@ -159,8 +158,8 @@ gimp_display_shell_profile_update (GimpDisplayShell *shell) if (shell->filter_transform || shell->profile_transform) { - gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH; - gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + gint w = shell->render_buf_width; + gint h = shell->render_buf_height; shell->profile_data = gegl_malloc (w * h * babl_format_get_bytes_per_pixel (src_format)); diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c index 0470d7b73f..7d0ea1af4a 100644 --- a/app/display/gimpdisplayshell-render.c +++ b/app/display/gimpdisplayshell-render.c @@ -28,9 +28,6 @@ #include "config/gimpdisplayconfig.h" -#include "gegl/gimp-gegl-utils.h" - -#include "core/gimpdrawable.h" #include "core/gimpimage.h" #include "core/gimppickable.h" #include "core/gimpprojectable.h" @@ -41,38 +38,129 @@ #include "gimpdisplayshell-filter.h" #include "gimpdisplayshell-profile.h" #include "gimpdisplayshell-render.h" -#include "gimpdisplayshell-scroll.h" -#include "gimpdisplayxfer.h" +void +gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy); +} + +void +gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->render_cache_valid) + { + cairo_rectangle_int_t rect; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + cairo_region_subtract_rectangle (shell->render_cache_valid, &rect); + } +} + +void +gimp_display_shell_render_validate_area (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->render_cache_valid) + { + cairo_rectangle_int_t rect; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + cairo_region_union_rectangle (shell->render_cache_valid, &rect); + } +} + +gboolean +gimp_display_shell_render_is_valid (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height) +{ + if (shell->render_cache_valid) + { + cairo_rectangle_int_t rect; + cairo_region_overlap_t overlap; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + overlap = cairo_region_contains_rectangle (shell->render_cache_valid, + &rect); + + return (overlap == CAIRO_REGION_OVERLAP_IN); + } + + return FALSE; +} + void gimp_display_shell_render (GimpDisplayShell *shell, cairo_t *cr, - gint x, - gint y, - gint w, - gint h, + gint tx, + gint ty, + gint twidth, + gint theight, gdouble scale) { - GimpImage *image; - GeglBuffer *buffer; + GimpImage *image; + GeglBuffer *buffer; #ifdef USE_NODE_BLIT - GeglNode *node; + GeglNode *node; #endif - cairo_surface_t *xfer; - gint xfer_src_x; - gint xfer_src_y; - gint mask_src_x = 0; - gint mask_src_y = 0; - gint cairo_stride; - guchar *cairo_data; + cairo_t *my_cr; + gint cairo_stride; + guchar *cairo_data; + gdouble x1, y1; + gdouble x2, y2; + gint x; + gint y; + gint width; + gint height; g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (cr != NULL); - g_return_if_fail (w > 0 && w <= GIMP_DISPLAY_RENDER_BUF_WIDTH); - g_return_if_fail (h > 0 && h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT); g_return_if_fail (scale > 0.0); + /* map chunk from screen space to scaled image space */ + gimp_display_shell_untransform_bounds_with_scale (shell, scale, + tx, ty, + tx + twidth, ty + theight, + &x1, &y1, + &x2, &y2); + + x = floor (x1); + y = floor (y1); + width = ceil (x2) - x; + height = ceil (y2) - y; + + g_return_if_fail (width > 0 && width <= shell->render_buf_width); + g_return_if_fail (height > 0 && height <= shell->render_buf_height); + image = gimp_display_get_image (shell->display); buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image)); #ifdef USE_NODE_BLIT @@ -81,12 +169,45 @@ gimp_display_shell_render (GimpDisplayShell *shell, gimp_projectable_begin_render (GIMP_PROJECTABLE (image)); #endif - xfer = gimp_display_xfer_get_surface (shell->xfer, w, h, - &xfer_src_x, &xfer_src_y); + if (! shell->render_surface) + { + shell->render_surface = + cairo_surface_create_similar_image (cairo_get_target (cr), + CAIRO_FORMAT_ARGB32, + shell->render_buf_width, + shell->render_buf_height); + } - cairo_stride = cairo_image_surface_get_stride (xfer); - cairo_data = cairo_image_surface_get_data (xfer) + - xfer_src_y * cairo_stride + xfer_src_x * 4; + cairo_surface_flush (shell->render_surface); + + if (! shell->render_cache) + { + shell->render_cache = + cairo_surface_create_similar_image (cairo_get_target (cr), + CAIRO_FORMAT_ARGB32, + shell->disp_width, + shell->disp_height); + } + + if (! shell->render_cache_valid) + { + shell->render_cache_valid = cairo_region_create (); + } + + my_cr = cairo_create (shell->render_cache); + + /* clip to chunk bounds, in screen space */ + cairo_rectangle (my_cr, tx, ty, twidth, theight); + cairo_clip (my_cr); + + /* transform to scaled image space, and apply uneven scaling */ + if (shell->rotate_transform) + cairo_transform (my_cr, shell->rotate_transform); + cairo_translate (my_cr, -shell->offset_x, -shell->offset_y); + cairo_scale (my_cr, shell->scale_x / scale, shell->scale_y / scale); + + cairo_stride = cairo_image_surface_get_stride (shell->render_surface); + cairo_data = cairo_image_surface_get_data (shell->render_surface); if (shell->profile_transform || gimp_display_shell_has_filter (shell)) @@ -105,8 +226,8 @@ gimp_display_shell_render (GimpDisplayShell *shell, if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) && ! shell->filter_buffer) { - gint fw = GIMP_DISPLAY_RENDER_BUF_WIDTH; - gint fh = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + gint fw = shell->render_buf_width; + gint fh = shell->render_buf_height; shell->filter_data = gegl_malloc (fw * fh * @@ -131,13 +252,13 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ #ifndef USE_NODE_BLIT gegl_buffer_get (buffer, - GEGL_RECTANGLE (x, y, w, h), scale, + GEGL_RECTANGLE (x, y, width, height), scale, gimp_projectable_get_format (GIMP_PROJECTABLE (image)), shell->profile_data, shell->profile_stride, GEGL_ABYSS_CLAMP); #else gegl_node_blit (node, - scale, GEGL_RECTANGLE (x, y, w, h), + scale, GEGL_RECTANGLE (x, y, width, height), gimp_projectable_get_format (GIMP_PROJECTABLE (image)), shell->profile_data, shell->profile_stride, GEGL_BLIT_CACHE); @@ -149,13 +270,13 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ #ifndef USE_NODE_BLIT gegl_buffer_get (buffer, - GEGL_RECTANGLE (x, y, w, h), scale, + GEGL_RECTANGLE (x, y, width, height), scale, shell->filter_format, shell->filter_data, shell->filter_stride, GEGL_ABYSS_CLAMP); #else gegl_node_blit (node, - scale, GEGL_RECTANGLE (x, y, w, h), + scale, GEGL_RECTANGLE (x, y, width, height), shell->filter_format, shell->filter_data, shell->filter_stride, GEGL_BLIT_CACHE); @@ -169,9 +290,11 @@ gimp_display_shell_render (GimpDisplayShell *shell, { gimp_color_transform_process_buffer (shell->filter_transform, shell->profile_buffer, - GEGL_RECTANGLE (0, 0, w, h), + GEGL_RECTANGLE (0, 0, + width, height), shell->filter_buffer, - GEGL_RECTANGLE (0, 0, w, h)); + GEGL_RECTANGLE (0, 0, + width, height)); } /* if there are filters, apply them @@ -194,7 +317,8 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ gimp_color_display_stack_convert_buffer (shell->filter_stack, filter_buffer, - GEGL_RECTANGLE (x, y, w, h)); + GEGL_RECTANGLE (x, y, + width, height)); g_object_unref (filter_buffer); } @@ -210,9 +334,11 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ gimp_color_transform_process_buffer (shell->profile_transform, shell->filter_buffer, - GEGL_RECTANGLE (0, 0, w, h), + GEGL_RECTANGLE (0, 0, + width, height), shell->filter_buffer, - GEGL_RECTANGLE (0, 0, w, h)); + GEGL_RECTANGLE (0, 0, + width, height)); } else if (! can_convert_to_u8) { @@ -221,16 +347,19 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ gimp_color_transform_process_buffer (shell->profile_transform, shell->profile_buffer, - GEGL_RECTANGLE (0, 0, w, h), + GEGL_RECTANGLE (0, 0, + width, height), shell->filter_buffer, - GEGL_RECTANGLE (0, 0, w, h)); + GEGL_RECTANGLE (0, 0, + width, height)); } else { GeglBuffer *buffer = gegl_buffer_linear_new_from_data (cairo_data, babl_format ("cairo-ARGB32"), - GEGL_RECTANGLE (0, 0, w, h), + GEGL_RECTANGLE (0, 0, + width, height), cairo_stride, NULL, NULL); @@ -239,9 +368,11 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ gimp_color_transform_process_buffer (shell->profile_transform, shell->profile_buffer, - GEGL_RECTANGLE (0, 0, w, h), + GEGL_RECTANGLE (0, 0, + width, height), buffer, - GEGL_RECTANGLE (0, 0, w, h)); + GEGL_RECTANGLE (0, 0, + width, height)); g_object_unref (buffer); } } @@ -252,7 +383,7 @@ gimp_display_shell_render (GimpDisplayShell *shell, if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) { gegl_buffer_get (shell->filter_buffer, - GEGL_RECTANGLE (0, 0, w, h), 1.0, + GEGL_RECTANGLE (0, 0, width, height), 1.0, babl_format ("cairo-ARGB32"), cairo_data, cairo_stride, GEGL_ABYSS_CLAMP); @@ -265,13 +396,13 @@ gimp_display_shell_render (GimpDisplayShell *shell, */ #ifndef USE_NODE_BLIT gegl_buffer_get (buffer, - GEGL_RECTANGLE (x, y, w, h), scale, + GEGL_RECTANGLE (x, y, width, height), scale, babl_format ("cairo-ARGB32"), cairo_data, cairo_stride, GEGL_ABYSS_CLAMP); #else gegl_node_blit (node, - scale, GEGL_RECTANGLE (x, y, w, h), + scale, GEGL_RECTANGLE (x, y, width, height), babl_format ("cairo-ARGB32"), cairo_data, cairo_stride, GEGL_BLIT_CACHE); @@ -282,26 +413,30 @@ gimp_display_shell_render (GimpDisplayShell *shell, gimp_projectable_end_render (GIMP_PROJECTABLE (image)); #endif + cairo_surface_mark_dirty (shell->render_surface); + + cairo_set_source_surface (my_cr, shell->render_surface, x, y); + cairo_paint (my_cr); + if (shell->mask) { if (! shell->mask_surface) { shell->mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, - GIMP_DISPLAY_RENDER_BUF_WIDTH, - GIMP_DISPLAY_RENDER_BUF_HEIGHT); + shell->render_buf_width, + shell->render_buf_height); } - cairo_surface_mark_dirty (shell->mask_surface); + cairo_surface_flush (shell->mask_surface); cairo_stride = cairo_image_surface_get_stride (shell->mask_surface); - cairo_data = cairo_image_surface_get_data (shell->mask_surface) + - mask_src_y * cairo_stride + mask_src_x; + cairo_data = cairo_image_surface_get_data (shell->mask_surface); gegl_buffer_get (shell->mask, GEGL_RECTANGLE (x - floor (shell->mask_offset_x * scale), y - floor (shell->mask_offset_y * scale), - w, h), + width, height), scale, babl_format ("Y u8"), cairo_data, cairo_stride, @@ -309,11 +444,11 @@ gimp_display_shell_render (GimpDisplayShell *shell, if (shell->mask_inverted) { - gint mask_height = h; + gint mask_height = height; while (mask_height--) { - gint mask_width = w; + gint mask_width = width; guchar *d = cairo_data; while (mask_width--) @@ -326,19 +461,12 @@ gimp_display_shell_render (GimpDisplayShell *shell, cairo_data += cairo_stride; } } + + cairo_surface_mark_dirty (shell->mask_surface); + + gimp_cairo_set_source_rgba (my_cr, &shell->mask_color); + cairo_mask_surface (my_cr, shell->mask_surface, x, y); } - /* put it to the screen */ - cairo_set_source_surface (cr, xfer, - x - xfer_src_x, - y - xfer_src_y); - cairo_paint (cr); - - if (shell->mask) - { - gimp_cairo_set_source_rgba (cr, &shell->mask_color); - cairo_mask_surface (cr, shell->mask_surface, - x - mask_src_x, - y - mask_src_y); - } + cairo_destroy (my_cr); } diff --git a/app/display/gimpdisplayshell-render.h b/app/display/gimpdisplayshell-render.h index 7b4a644cfb..31898a211e 100644 --- a/app/display/gimpdisplayshell-render.h +++ b/app/display/gimpdisplayshell-render.h @@ -18,12 +18,37 @@ #ifndef __GIMP_DISPLAY_SHELL_RENDER_H__ #define __GIMP_DISPLAY_SHELL_RENDER_H__ -void gimp_display_shell_render (GimpDisplayShell *shell, - cairo_t *cr, - gint x, - gint y, - gint w, - gint h, - gdouble scale); + +#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 +#define GIMP_DISPLAY_RENDER_MAX_SCALE 4.0 + + +void gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell); +void gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height); + +void gimp_display_shell_render_validate_area (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height); + +gboolean gimp_display_shell_render_is_valid (GimpDisplayShell *shell, + gint x, + gint y, + gint width, + gint height); + +void gimp_display_shell_render (GimpDisplayShell *shell, + cairo_t *cr, + gint x, + gint y, + gint width, + gint height, + gdouble scale); + #endif /* __GIMP_DISPLAY_SHELL_RENDER_H__ */ diff --git a/app/display/gimpdisplayshell-rotate.c b/app/display/gimpdisplayshell-rotate.c index dd9ed6940f..59e71c1134 100644 --- a/app/display/gimpdisplayshell-rotate.c +++ b/app/display/gimpdisplayshell-rotate.c @@ -29,6 +29,7 @@ #include "gimpdisplay.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-rotate.h" #include "gimpdisplayshell-scale.h" #include "gimpdisplayshell-scroll.h" @@ -84,6 +85,7 @@ gimp_display_shell_flip (GimpDisplayShell *shell, gimp_display_shell_restore_viewport_center (shell, cx, cy); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } } @@ -121,6 +123,7 @@ gimp_display_shell_rotate_to (GimpDisplayShell *shell, gimp_display_shell_restore_viewport_center (shell, cx, cy); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } void diff --git a/app/display/gimpdisplayshell-scale.c b/app/display/gimpdisplayshell-scale.c index 73811e664e..dec74fb885 100644 --- a/app/display/gimpdisplayshell-scale.c +++ b/app/display/gimpdisplayshell-scale.c @@ -36,6 +36,7 @@ #include "gimpdisplay.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-rotate.h" #include "gimpdisplayshell-scale.h" #include "gimpdisplayshell-scroll.h" @@ -719,6 +720,7 @@ gimp_display_shell_scale_resize (GimpDisplayShell *shell, gimp_display_shell_scaled (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); /* re-enable the active tool */ gimp_display_shell_resume (shell); diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c index 1987d5d9a4..d120f4c2ab 100644 --- a/app/display/gimpdisplayshell-scroll.c +++ b/app/display/gimpdisplayshell-scroll.c @@ -36,6 +36,7 @@ #include "gimpdisplay-foreach.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-render.h" #include "gimpdisplayshell-rotate.h" #include "gimpdisplayshell-rulers.h" #include "gimpdisplayshell-scale.h" @@ -94,6 +95,45 @@ gimp_display_shell_scroll (GimpDisplayShell *shell, gimp_overlay_box_scroll (GIMP_OVERLAY_BOX (shell->canvas), -x_offset, -y_offset); + if (shell->render_cache) + { + cairo_surface_t *surface; + cairo_t *cr; + + surface = + cairo_surface_create_similar_image (shell->render_cache, + CAIRO_FORMAT_ARGB32, + shell->disp_width, + shell->disp_height); + + cr = cairo_create (surface); + cairo_set_source_surface (cr, shell->render_cache, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); + + cr = cairo_create (shell->render_cache); + cairo_set_source_surface (cr, surface, + -x_offset, -y_offset); + cairo_paint (cr); + cairo_destroy (cr); + + cairo_surface_destroy (surface); + } + + if (shell->render_cache_valid) + { + cairo_rectangle_int_t rect; + + cairo_region_translate (shell->render_cache_valid, + -x_offset, -y_offset); + + rect.x = 0; + rect.y = 0; + rect.width = shell->disp_width; + rect.height = shell->disp_height; + + cairo_region_intersect_rectangle (shell->render_cache_valid, &rect); + } } /* re-enable the active tool */ @@ -137,6 +177,7 @@ gimp_display_shell_scroll_set_offset (GimpDisplayShell *shell, gimp_display_shell_scrolled (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); /* re-enable the active tool */ gimp_display_shell_resume (shell); diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c index b029487787..a1075cb66f 100644 --- a/app/display/gimpdisplayshell.c +++ b/app/display/gimpdisplayshell.c @@ -723,8 +723,12 @@ gimp_display_shell_dispose (GObject *object) shell->filter_idle_id = 0; } - g_clear_pointer (&shell->mask_surface, cairo_surface_destroy); - g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy); + g_clear_pointer (&shell->render_cache, cairo_surface_destroy); + g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy); + + g_clear_pointer (&shell->render_surface, cairo_surface_destroy); + g_clear_pointer (&shell->mask_surface, cairo_surface_destroy); + g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy); gimp_display_shell_profile_finalize (shell); @@ -1032,6 +1036,7 @@ gimp_display_shell_profile_changed (GimpColorManaged *managed) gimp_display_shell_profile_update (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } static void @@ -1312,6 +1317,7 @@ gimp_display_shell_reconnect (GimpDisplayShell *shell) gimp_display_shell_scaled (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } static gboolean @@ -1377,6 +1383,7 @@ gimp_display_shell_empty (GimpDisplayShell *shell) gimp_display_shell_rotate_update_transform (shell); gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); user_context = gimp_get_user_context (shell->display->gimp); @@ -1867,4 +1874,5 @@ gimp_display_shell_set_mask (GimpDisplayShell *shell, shell->mask_inverted = inverted; gimp_display_shell_expose_full (shell); + gimp_display_shell_render_invalidate_full (shell); } diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h index a96f67aa76..00176344aa 100644 --- a/app/display/gimpdisplayshell.h +++ b/app/display/gimpdisplayshell.h @@ -166,7 +166,13 @@ struct _GimpDisplayShell guchar *filter_data; /* filter_buffer's pixels */ gint filter_stride; /* filter_buffer's stride */ - GimpDisplayXfer *xfer; /* manages image buffer transfers */ + cairo_surface_t *render_cache; + cairo_region_t *render_cache_valid; + + gint render_buf_width; + gint render_buf_height; + + cairo_surface_t *render_surface; /* buffer for rendering the mask */ cairo_surface_t *mask_surface; /* buffer for rendering the mask */ cairo_pattern_t *checkerboard; /* checkerboard pattern */ diff --git a/app/display/gimpdisplayxfer.c b/app/display/gimpdisplayxfer.c deleted file mode 100644 index 9dfdbf8fcd..0000000000 --- a/app/display/gimpdisplayxfer.c +++ /dev/null @@ -1,300 +0,0 @@ -/* 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 -#include - -#include "display-types.h" - -#include "gimpdisplayxfer.h" - - -#define NUM_PAGES 2 - -typedef struct _RTree RTree; -typedef struct _RTreeNode RTreeNode; - -struct _RTreeNode -{ - RTreeNode *children[2]; - RTreeNode *next; - gint x, y, w, h; -}; - -struct _RTree -{ - RTreeNode root; - RTreeNode *available; -}; - -struct _GimpDisplayXfer -{ - /* track subregions of render_surface for efficient uploads */ - RTree rtree; - cairo_surface_t *render_surface[NUM_PAGES]; - gint page; -}; - - -gint GIMP_DISPLAY_RENDER_BUF_WIDTH = 256; -gint GIMP_DISPLAY_RENDER_BUF_HEIGHT = 256; - - -static RTreeNode * -rtree_node_create (RTree *rtree, - RTreeNode **prev, - gint x, - gint y, - gint w, - gint h) -{ - RTreeNode *node; - - gimp_assert (x >= 0 && x+w <= rtree->root.w); - gimp_assert (y >= 0 && y+h <= rtree->root.h); - - if (w <= 0 || h <= 0) - return NULL; - - node = g_slice_alloc (sizeof (*node)); - - 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 (RTree *rtree, - RTreeNode *node) -{ - gint i; - - for (i = 0; i < 2; i++) - { - if (node->children[i]) - rtree_node_destroy (rtree, node->children[i]); - } - - g_slice_free (RTreeNode, node); -} - -static RTreeNode * -rtree_node_insert (RTree *rtree, - RTreeNode **prev, - RTreeNode *node, - gint w, - gint h) -{ - *prev = node->next; - - if (((node->w - w) | (node->h - h)) > 1) - { - gint ww = node->w - w; - gint 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 RTreeNode * -rtree_insert (RTree *rtree, - gint w, - gint h) -{ - RTreeNode *node, **prev; - - 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 (RTree *rtree, - gint w, - gint 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 (RTree *rtree) -{ - gint 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; - const gchar *env; - - env = g_getenv ("GIMP_DISPLAY_RENDER_BUF_SIZE"); - if (env) - { - gint width = atoi (env); - gint height = width; - - env = strchr (env, 'x'); - if (env) - height = atoi (env + 1); - - if (width > 0 && width <= 8192 && - height > 0 && height <= 8192) - { - GIMP_DISPLAY_RENDER_BUF_WIDTH = width; - GIMP_DISPLAY_RENDER_BUF_HEIGHT = height; - } - } - - screen = gtk_widget_get_screen (widget); - xfer = g_object_get_data (G_OBJECT (screen), "gimp-display-xfer"); - - if (xfer == NULL) - { - GdkDrawingContext *context; - cairo_region_t *region; - cairo_t *cr; - gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH; - gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT; - int n; - - xfer = g_new (GimpDisplayXfer, 1); - rtree_init (&xfer->rtree, w, h); - - region = cairo_region_create (); - context = gdk_window_begin_draw_frame (gtk_widget_get_window (widget), - region); - cairo_region_destroy (region); - - cr = gdk_drawing_context_get_cairo_context (context); - - 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]); - } - - gdk_window_end_draw_frame (gtk_widget_get_window (widget), - context); - - xfer->page = 0; - - g_object_set_data_full (G_OBJECT (screen), - "gimp-display-xfer", - 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) -{ - RTreeNode *node; - - gimp_assert (w <= GIMP_DISPLAY_RENDER_BUF_WIDTH && - h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT); - - 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); - gimp_assert (node != NULL); - } - - *src_x = node->x; - *src_y = node->y; - - return xfer->render_surface[xfer->page]; -} diff --git a/app/display/gimpdisplayxfer.h b/app/display/gimpdisplayxfer.h deleted file mode 100644 index 20656b90b4..0000000000 --- a/app/display/gimpdisplayxfer.h +++ /dev/null @@ -1,37 +0,0 @@ -/* 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_XFER_H__ -#define __GIMP_DISPLAY_XFER_H__ - - -extern gint GIMP_DISPLAY_RENDER_BUF_WIDTH; -extern gint GIMP_DISPLAY_RENDER_BUF_HEIGHT; - -#define GIMP_DISPLAY_RENDER_MAX_SCALE 4.0 - - -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_XFER_H__ */