[gimp/metadata-browser] app: add GimpTileHandlerProjection and use it to validate the projection



commit a1d00d1d5510cff803d00dbcb056714af4f2c870
Author: Michael Natterer <mitch gimp org>
Date:   Thu Jul 5 21:42:26 2012 +0200

    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.

 app/core/gimpprojection.c             |  144 ++++-----------
 app/core/gimpprojection.h             |   12 +-
 app/display/gimpdisplayshell-render.c |   61 +++----
 app/display/gimpdisplayshell.c        |    5 +-
 app/file/file-open.c                  |    4 +
 app/gegl/Makefile.am                  |    4 +-
 app/gegl/gimptilehandlerprojection.c  |  325 +++++++++++++++++++++++++++++++++
 app/gegl/gimptilehandlerprojection.h  |   70 +++++++
 8 files changed, 465 insertions(+), 160 deletions(-)
---
diff --git a/app/core/gimpprojection.c b/app/core/gimpprojection.c
index dfa89b7..b8cee63 100644
--- a/app/core/gimpprojection.c
+++ b/app/core/gimpprojection.c
@@ -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;
 
@@ -438,6 +377,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,
                                  gint            y,
@@ -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);
diff --git a/app/core/gimpprojection.h b/app/core/gimpprojection.h
index 657fd40..95040d7 100644
--- a/app/core/gimpprojection.h
+++ b/app/core/gimpprojection.h
@@ -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);
diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c
index f68327f..252e4d4 100644
--- a/app/display/gimpdisplayshell-render.c
+++ b/app/display/gimpdisplayshell-render.c
@@ -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);
 }
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index aca40f1..4479dd3 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -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
diff --git a/app/file/file-open.c b/app/file/file-open.c
index e4f8d59..478776f 100644
--- a/app/file/file-open.c
+++ b/app/file/file-open.c
@@ -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 */
diff --git a/app/gegl/Makefile.am b/app/gegl/Makefile.am
index e1fa3ca..4a90b1e 100644
--- a/app/gegl/Makefile.am
+++ b/app/gegl/Makefile.am
@@ -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
 
diff --git a/app/gegl/gimptilehandlerprojection.c b/app/gegl/gimptilehandlerprojection.c
new file mode 100644
index 0000000..c2a4808
--- /dev/null
+++ b/app/gegl/gimptilehandlerprojection.c
@@ -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);
+        }
+    }
+}
diff --git a/app/gegl/gimptilehandlerprojection.h b/app/gegl/gimptilehandlerprojection.h
new file mode 100644
index 0000000..eec9980
--- /dev/null
+++ b/app/gegl/gimptilehandlerprojection.h
@@ -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__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]