app: add GimpTileHandlerProjection and use it to validate the projection

as the projection buffer is being read from. Projection performance is
now back at its old speed.
This commit is contained in:
Michael Natterer 2012-07-05 21:42:26 +02:00
parent c743a4fc0a
commit 6b6d39fc64
8 changed files with 465 additions and 160 deletions

View File

@ -17,11 +17,13 @@
#include "config.h"
#include <cairo.h>
#include <gegl.h>
#include "core-types.h"
#include "gegl/gimp-gegl-utils.h"
#include "gegl/gimptilehandlerprojection.h"
#include "gimp.h"
#include "gimp-utils.h"
@ -66,6 +68,7 @@ static gdouble gimp_projection_get_opacity_at (GimpPickable *picka
gint x,
gint y);
static void gimp_projection_free_buffer (GimpProjection *proj);
static void gimp_projection_add_update_area (GimpProjection *proj,
gint x,
gint y,
@ -138,11 +141,6 @@ gimp_projection_class_init (GimpProjectionClass *klass)
static void
gimp_projection_init (GimpProjection *proj)
{
proj->projectable = NULL;
proj->buffer = NULL;
proj->update_areas = NULL;
proj->idle_render.idle_id = 0;
proj->idle_render.update_areas = NULL;
}
static void
@ -174,24 +172,7 @@ gimp_projection_finalize (GObject *object)
gimp_area_list_free (proj->idle_render.update_areas);
proj->idle_render.update_areas = NULL;
if (proj->buffer)
{
g_object_unref (proj->buffer);
proj->buffer = NULL;
}
if (proj->graph)
{
g_object_unref (proj->graph);
proj->graph = NULL;
proj->sink_node = NULL;
}
if (proj->processor)
{
g_object_unref (proj->processor);
proj->processor = NULL;
}
gimp_projection_free_buffer (proj);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -286,22 +267,22 @@ gimp_projection_get_buffer (GimpPickable *pickable)
if (! proj->buffer)
{
GeglNode *graph;
const Babl *format;
gint width;
gint height;
graph = gimp_projectable_get_graph (proj->projectable);
format = gimp_projection_get_format (GIMP_PICKABLE (proj));
gimp_projectable_get_size (proj->projectable, &width, &height);
proj->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
format);
if (proj->sink_node)
{
gegl_node_set (proj->sink_node,
"buffer", proj->buffer,
NULL);
}
proj->validate_handler = gimp_tile_handler_projection_new (graph);
gegl_buffer_add_handler (proj->buffer, proj->validate_handler);
gimp_tile_handler_projection_invalidate (proj->validate_handler,
0, 0, width, height);
}
return proj->buffer;
@ -360,44 +341,6 @@ gimp_projection_new (GimpProjectable *projectable)
return proj;
}
#ifdef USE_BUFFER
GeglNode *
gimp_projection_get_sink_node (GimpProjection *proj)
{
GeglNode *graph;
GeglBuffer *buffer;
g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL);
if (proj->sink_node)
return proj->sink_node;
proj->graph = gegl_node_new ();
#if 0
g_object_set (proj->graph,
"dont-cache", TRUE,
NULL);
#endif
graph = gimp_projectable_get_graph (proj->projectable);
gegl_node_add_child (proj->graph, graph);
buffer = gimp_projection_get_buffer (GIMP_PICKABLE (proj));
proj->sink_node =
gegl_node_new_child (proj->graph,
"operation", "gegl:write-buffer",
"buffer", buffer,
NULL);
gegl_node_connect_to (graph, "output",
proj->sink_node, "input");
return proj->sink_node;
}
#endif
void
gimp_projection_flush (GimpProjection *proj)
{
@ -423,10 +366,6 @@ gimp_projection_finish_draw (GimpProjection *proj)
if (proj->idle_render.idle_id)
{
#if 0
g_printerr ("%s: flushing idle render queue\n", G_STRFUNC);
#endif
g_source_remove (proj->idle_render.idle_id);
proj->idle_render.idle_id = 0;
@ -437,6 +376,25 @@ gimp_projection_finish_draw (GimpProjection *proj)
/* private functions */
static void
gimp_projection_free_buffer (GimpProjection *proj)
{
if (proj->buffer)
{
if (proj->validate_handler)
gegl_buffer_remove_handler (proj->buffer, proj->validate_handler);
g_object_unref (proj->buffer);
proj->buffer = NULL;
}
if (proj->validate_handler)
{
g_object_unref (proj->validate_handler);
proj->validate_handler = NULL;
}
}
static void
gimp_projection_add_update_area (GimpProjection *proj,
gint x,
@ -695,36 +653,6 @@ gimp_projection_paint_area (GimpProjection *proj,
y2 - y1);
}
#ifdef USE_BUFFER
static void
gimp_projection_construct (GimpProjection *proj,
gint x,
gint y,
gint w,
gint h)
{
GeglRectangle rect = { x, y, w, h };
g_return_if_fail (GIMP_IS_PROJECTION (proj));
/* GEGL should really do this for us... */
gegl_buffer_clear (gimp_projection_get_buffer (GIMP_PICKABLE (proj)), &rect);
if (! proj->processor)
{
GeglNode *sink = gimp_projection_get_sink_node (proj);
proj->processor = gegl_node_new_processor (sink, &rect);
}
else
{
gegl_processor_set_rectangle (proj->processor, &rect);
}
while (gegl_processor_work (proj->processor, NULL));
}
#endif
static void
gimp_projection_invalidate (GimpProjection *proj,
guint x,
@ -732,11 +660,9 @@ gimp_projection_invalidate (GimpProjection *proj,
guint w,
guint h)
{
/* FIXME: this should happen as we actually *read* from the buffer
*/
#ifdef USE_BUFFER
gimp_projection_construct (proj, x, y, w, h);
#endif
if (proj->validate_handler)
gimp_tile_handler_projection_invalidate (proj->validate_handler,
x, y, w, h);
}
@ -780,11 +706,7 @@ gimp_projection_projectable_changed (GimpProjectable *projectable,
gimp_area_list_free (proj->update_areas);
proj->update_areas = NULL;
if (proj->buffer)
{
g_object_unref (proj->buffer);
proj->buffer = NULL;
}
gimp_projection_free_buffer (proj);
gimp_projectable_get_offset (proj->projectable, &off_x, &off_y);
gimp_projectable_get_size (projectable, &width, &height);

View File

@ -19,9 +19,6 @@
#define __GIMP_PROJECTION_H__
#define USE_BUFFER 1
#include "gimpobject.h"
@ -58,10 +55,7 @@ struct _GimpProjection
GimpProjectable *projectable;
GeglBuffer *buffer;
GeglNode *graph;
GeglNode *sink_node;
GeglProcessor *processor;
gpointer validate_handler;
GSList *update_areas;
GimpProjectionIdleRender idle_render;
@ -86,10 +80,6 @@ GType gimp_projection_get_type (void) G_GNUC_CONST;
GimpProjection * gimp_projection_new (GimpProjectable *projectable);
#if USE_BUFFER
GeglNode * gimp_projection_get_sink_node (GimpProjection *proj);
#endif
void gimp_projection_flush (GimpProjection *proj);
void gimp_projection_flush_now (GimpProjection *proj);
void gimp_projection_finish_draw (GimpProjection *proj);

View File

@ -51,42 +51,37 @@ gimp_display_shell_render (GimpDisplayShell *shell,
gint w,
gint h)
{
#ifdef USE_BUFFER
GimpImage *image;
GimpProjection *projection;
GeglBuffer *buffer;
#endif
GimpImage *image;
gint viewport_offset_x;
gint viewport_offset_y;
gint viewport_width;
gint viewport_height;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (cr != NULL);
g_return_if_fail (w > 0 && h > 0);
image = gimp_display_get_image (shell->display);
#ifdef USE_BUFFER
image = gimp_display_get_image (shell->display);
projection = gimp_image_get_projection (image);
buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (projection));
buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (projection));
gimp_display_shell_scroll_get_scaled_viewport (shell,
&viewport_offset_x,
&viewport_offset_y,
&viewport_width,
&viewport_height);
gegl_buffer_get (buffer,
GEGL_RECTANGLE (x + shell->offset_x,
y + shell->offset_y,
GEGL_RECTANGLE (x + viewport_offset_x,
y + viewport_offset_y,
w, h),
shell->scale_x,
babl_format ("cairo-ARGB32"),
cairo_image_surface_get_data (shell->render_surface),
cairo_image_surface_get_stride (shell->render_surface),
GEGL_ABYSS_NONE);
#else
gegl_node_blit (gimp_projectable_get_graph (GIMP_PROJECTABLE (image)),
shell->scale_x,
GEGL_RECTANGLE (src_x * shell->scale_x,
src_y * shell->scale_y,
w, h),
babl_format ("cairo-ARGB32"),
cairo_image_surface_get_data (shell->render_surface),
cairo_image_surface_get_stride (shell->render_surface),
0);
#endif
/* apply filters to the rendered projection */
if (shell->filter_stack)
@ -136,24 +131,22 @@ gimp_display_shell_render (GimpDisplayShell *shell,
#endif
/* put it to the screen */
{
cairo_save (cr);
cairo_save (cr);
cairo_rectangle (cr, x, y, w, h);
cairo_clip (cr);
cairo_rectangle (cr, x, y, w, h);
cairo_clip (cr);
cairo_set_source_surface (cr, shell->render_surface, x, y);
cairo_paint (cr);
cairo_set_source_surface (cr, shell->render_surface, x, y);
cairo_paint (cr);
#if 0
if (shell->mask)
{
gimp_cairo_set_source_rgba (cr, &shell->mask_color);
cairo_mask_surface (cr, shell->mask_surface,
x + disp_xoffset, y + disp_yoffset);
}
if (shell->mask)
{
gimp_cairo_set_source_rgba (cr, &shell->mask_color);
cairo_mask_surface (cr, shell->mask_surface,
x + disp_xoffset, y + disp_yoffset);
}
#endif
cairo_restore (cr);
}
cairo_restore (cr);
}

View File

@ -1388,9 +1388,8 @@ gimp_display_shell_fill (GimpDisplayShell *shell,
/* we double buffer image drawing manually */
gtk_widget_set_double_buffered (shell->canvas, FALSE);
shell->fill_idle_id = g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) gimp_display_shell_fill_idle,
shell, NULL);
shell->fill_idle_id = g_idle_add ((GSourceFunc) gimp_display_shell_fill_idle,
shell);
}
/* We used to calculate the scale factor in the SCALEFACTOR_X() and

View File

@ -658,6 +658,9 @@ file_open_sanitize_image (GimpImage *image,
*/
gimp_image_clean_all (image);
#if 0
/* XXX this is not needed any longer, remove it when sure */
/* make sure the entire projection is properly constructed, because
* load plug-ins are not required to call gimp_drawable_update() or
* anything.
@ -670,6 +673,7 @@ file_open_sanitize_image (GimpImage *image,
/* same for drawable previews */
gimp_image_invalidate_previews (image);
#endif
}
/* Converts items from one image to another */

View File

@ -35,7 +35,9 @@ libappgegl_a_sources = \
gimp-gegl-utils.c \
gimp-gegl-utils.h \
gimpapplicator.c \
gimpapplicator.h
gimpapplicator.h \
gimptilehandlerprojection.c \
gimptilehandlerprojection.h
libappgegl_a_built_sources = gimp-gegl-enums.c

View File

@ -0,0 +1,325 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <cairo.h>
#include <gegl.h>
#include "gimp-gegl-types.h"
#include "gimptilehandlerprojection.h"
enum
{
PROP_0,
PROP_FORMAT,
PROP_TILE_WIDTH,
PROP_TILE_HEIGHT
};
static void gimp_tile_handler_projection_finalize (GObject *object);
static void gimp_tile_handler_projection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_tile_handler_projection_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gpointer gimp_tile_handler_projection_command (GeglTileSource *source,
GeglTileCommand command,
gint x,
gint y,
gint z,
gpointer data);
G_DEFINE_TYPE (GimpTileHandlerProjection, gimp_tile_handler_projection,
GEGL_TYPE_TILE_HANDLER)
#define parent_class gimp_tile_handler_projection_parent_class
static void
gimp_tile_handler_projection_class_init (GimpTileHandlerProjectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gimp_tile_handler_projection_finalize;
object_class->set_property = gimp_tile_handler_projection_set_property;
object_class->get_property = gimp_tile_handler_projection_get_property;
g_object_class_install_property (object_class, PROP_FORMAT,
g_param_spec_pointer ("format", NULL, NULL,
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_TILE_WIDTH,
g_param_spec_int ("tile-width", NULL, NULL,
1, G_MAXINT, 1,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_TILE_HEIGHT,
g_param_spec_int ("tile-height", NULL, NULL,
1, G_MAXINT, 1,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
gimp_tile_handler_projection_init (GimpTileHandlerProjection *projection)
{
GeglTileSource *source = GEGL_TILE_SOURCE (projection);
source->command = gimp_tile_handler_projection_command;
projection->dirty_region = cairo_region_create ();
}
static void
gimp_tile_handler_projection_finalize (GObject *object)
{
GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object);
if (projection->graph)
{
g_object_unref (projection->graph);
projection->graph = NULL;
}
cairo_region_destroy (projection->dirty_region);
projection->dirty_region = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_tile_handler_projection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object);
switch (property_id)
{
case PROP_FORMAT:
projection->format = g_value_get_pointer (value);
break;
case PROP_TILE_WIDTH:
projection->tile_width = g_value_get_int (value);
break;
case PROP_TILE_HEIGHT:
projection->tile_height = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_tile_handler_projection_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object);
switch (property_id)
{
case PROP_FORMAT:
g_value_set_pointer (value, (gpointer) projection->format);
break;
case PROP_TILE_WIDTH:
g_value_set_int (value, projection->tile_width);
break;
case PROP_TILE_HEIGHT:
g_value_set_int (value, projection->tile_height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GeglTile *
gimp_tile_handler_projection_validate (GeglTileSource *source,
GeglTile *tile,
gint x,
gint y)
{
GimpTileHandlerProjection *projection;
cairo_region_t *tile_region;
cairo_rectangle_int_t tile_rect;
projection = GIMP_TILE_HANDLER_PROJECTION (source);
if (cairo_region_is_empty (projection->dirty_region))
return tile;
tile_region = cairo_region_copy (projection->dirty_region);
tile_rect.x = x * projection->tile_width;
tile_rect.y = y * projection->tile_height;
tile_rect.width = projection->tile_width;
tile_rect.height = projection->tile_height;
cairo_region_intersect_rectangle (tile_region, &tile_rect);
if (! cairo_region_is_empty (tile_region))
{
gint tile_bpp;
gint tile_stride;
gint n_rects;
gint i;
if (! tile)
tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source),
x, y, 0);
cairo_region_subtract_rectangle (projection->dirty_region, &tile_rect);
tile_bpp = babl_format_get_bytes_per_pixel (projection->format);
tile_stride = tile_bpp * projection->tile_width;
gegl_tile_lock (tile);
n_rects = cairo_region_num_rectangles (tile_region);
#if 0
g_printerr ("%d ", n_rects);
#endif
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t blit_rect;
cairo_region_get_rectangle (tile_region, i, &blit_rect);
#if 0
g_printerr ("constructing projection at %d %d %d %d\n",
blit_rect.x,
blit_rect.y,
blit_rect.width,
blit_rect.height);
#endif
gegl_node_blit (projection->graph, 1.0,
GEGL_RECTANGLE (blit_rect.x,
blit_rect.y,
blit_rect.width,
blit_rect.height),
projection->format,
gegl_tile_get_data (tile) +
(blit_rect.y % projection->tile_height) * tile_stride +
(blit_rect.x % projection->tile_width) * tile_bpp,
tile_stride,
GEGL_ABYSS_NONE);
}
gegl_tile_unlock (tile);
}
cairo_region_destroy (tile_region);
return tile;
}
static gpointer
gimp_tile_handler_projection_command (GeglTileSource *source,
GeglTileCommand command,
gint x,
gint y,
gint z,
gpointer data)
{
gpointer retval;
retval = gegl_tile_handler_source_command (source, command, x, y, z, data);
if (command == GEGL_TILE_GET && z == 0)
retval = gimp_tile_handler_projection_validate (source, retval, x, y);
return retval;
}
GeglTileHandler *
gimp_tile_handler_projection_new (GeglNode *graph)
{
GimpTileHandlerProjection *projection;
g_return_val_if_fail (GEGL_IS_NODE (graph), NULL);
projection = g_object_new (GIMP_TYPE_TILE_HANDLER_PROJECTION, NULL);
projection->graph = g_object_ref (graph);
return GEGL_TILE_HANDLER (projection);
}
static void
gimp_tile_handler_projection_void_pyramid (GeglTileSource *source,
gint x,
gint y,
gint z)
{
gegl_tile_source_void (source, x, y, z);
if (x / 2 != x || y / 2 != y)
gimp_tile_handler_projection_void_pyramid (source, x / 2, y / 2, z + 1);
}
void
gimp_tile_handler_projection_invalidate (GimpTileHandlerProjection *projection,
gint x,
gint y,
gint width,
gint height)
{
cairo_rectangle_int_t rect = { x, y, width, height };
gint tile_x1;
gint tile_y1;
gint tile_x2;
gint tile_y2;
gint tile_x;
gint tile_y;
g_return_if_fail (GIMP_IS_TILE_HANDLER_PROJECTION (projection));
cairo_region_union_rectangle (projection->dirty_region, &rect);
tile_x1 = x / projection->tile_width;
tile_y1 = y / projection->tile_height;
tile_x2 = (x + width) / projection->tile_width;
tile_y2 = (y + height) / projection->tile_height;
for (tile_y = tile_y1; tile_y <= tile_y2; tile_y++)
{
for (tile_x = tile_x1; tile_x <= tile_x2; tile_x++)
{
gimp_tile_handler_projection_void_pyramid (GEGL_TILE_SOURCE (projection),
tile_x / 2, tile_y / 2, 1);
}
}
}

View File

@ -0,0 +1,70 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_TILE_HANDLER_PROJECTION_H__
#define __GIMP_TILE_HANDLER_PROJECTION_H__
#include <gegl-buffer-backend.h>
/***
* GimpTileHandlerProjection is a GeglTileHandler that renders the
* projection.
*/
G_BEGIN_DECLS
#define GIMP_TYPE_TILE_HANDLER_PROJECTION (gimp_tile_handler_projection_get_type ())
#define GIMP_TILE_HANDLER_PROJECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTION, GimpTileHandlerProjection))
#define GIMP_TILE_HANDLER_PROJECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TILE_HANDLER_PROJECTION, GimpTileHandlerProjectionClass))
#define GIMP_IS_TILE_HANDLER_PROJECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTION))
#define GIMP_IS_TILE_HANDLER_PROJECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TILE_HANDLER_PROJECTION))
#define GIMP_TILE_HANDLER_PROJECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTION, GimpTileHandlerProjectionClass))
typedef struct _GimpTileHandlerProjection GimpTileHandlerProjection;
typedef struct _GimpTileHandlerProjectionClass GimpTileHandlerProjectionClass;
struct _GimpTileHandlerProjection
{
GeglTileHandler parent_instance;
GeglNode *graph;
cairo_region_t *dirty_region;
const Babl *format;
gint tile_width;
gint tile_height;
};
struct _GimpTileHandlerProjectionClass
{
GeglTileHandlerClass parent_class;
};
GType gimp_tile_handler_projection_get_type (void) G_GNUC_CONST;
GeglTileHandler * gimp_tile_handler_projection_new (GeglNode *graph);
void gimp_tile_handler_projection_invalidate (GimpTileHandlerProjection *projection,
gint x,
gint y,
gint width,
gint height);
G_END_DECLS
#endif /* __GIMP_TILE_HANDLER_PROJECTION_H__ */