[gimp/gimp-2-10] app: add support for projectables with an arbitrary bounding box



commit 1359c1cb47c8a6efc1a340575ae95748b1a21482
Author: Ell <ell_se yahoo com>
Date:   Thu Aug 1 23:01:58 2019 +0300

    app: add support for projectables with an arbitrary bounding box
    
    In GimpProjectable, replace gimp_projectable_get_size(), which only
    returned a width and a height, with
    gimp_projectable_get_bounding_box(), which returns a full
    rectangle.  This allows projectables to have an arbitrary bounding
    box, not limited to a (0, 0) top-left corner.
    
    Adapt GimpProjection, creating a buffer with corresponding extent
    to the projectable's bounding box.
    
    Adapt GimpImage and GimpGroupLayer.
    
    (cherry picked from commit 8ff43942d6ea6dc7ef34200249e28861de0573e0)

 app/core/gimpgrouplayer.c  |  53 ++++++++------
 app/core/gimpimage.c       |  13 +++-
 app/core/gimpprojectable.c |  30 +++-----
 app/core/gimpprojectable.h |  16 ++---
 app/core/gimpprojection.c  | 173 ++++++++++++++++++++++++++++-----------------
 5 files changed, 170 insertions(+), 115 deletions(-)
---
diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c
index c71ff9141f..c00ccb8f66 100644
--- a/app/core/gimpgrouplayer.c
+++ b/app/core/gimpgrouplayer.c
@@ -178,6 +178,8 @@ static gboolean
               gimp_group_layer_get_excludes_backdrop (GimpLayer         *layer);
 
 static const Babl    * gimp_group_layer_get_format   (GimpProjectable *projectable);
+static GeglRectangle
+                   gimp_group_layer_get_bounding_box (GimpProjectable *projectable);
 static GeglNode      * gimp_group_layer_get_graph    (GimpProjectable *projectable);
 static void            gimp_group_layer_begin_render (GimpProjectable *projectable);
 static void            gimp_group_layer_end_render   (GimpProjectable *projectable);
@@ -312,7 +314,7 @@ gimp_projectable_iface_init (GimpProjectableInterface *iface)
   iface->get_image          = (GimpImage * (*) (GimpProjectable *)) gimp_item_get_image;
   iface->get_format         = gimp_group_layer_get_format;
   iface->get_offset         = (void (*) (GimpProjectable*, gint*, gint*)) gimp_item_get_offset;
-  iface->get_size           = (void (*) (GimpProjectable*, gint*, gint*)) gimp_viewable_get_size;
+  iface->get_bounding_box   = gimp_group_layer_get_bounding_box;
   iface->get_graph          = gimp_group_layer_get_graph;
   iface->begin_render       = gimp_group_layer_begin_render;
   iface->end_render         = gimp_group_layer_end_render;
@@ -457,29 +459,16 @@ gimp_group_layer_get_size (GimpViewable *viewable,
                            gint         *height)
 {
   GimpGroupLayerPrivate *private = GET_PRIVATE (viewable);
-  gboolean               result;
 
-  if (private->reallocate_width  != 0 &&
-      private->reallocate_height != 0)
+  /*  return the size only if there are children ...  */
+  if (! gimp_container_is_empty (private->children))
     {
-      *width  = private->reallocate_width;
-      *height = private->reallocate_height;
-
-      return TRUE;
+      return GIMP_VIEWABLE_CLASS (parent_class)->get_size (viewable,
+                                                           width, height);
     }
 
-  result = GIMP_VIEWABLE_CLASS (parent_class)->get_size (viewable,
-                                                         width, height);
-
-  /* if the group is empty, return "no content" through
-   * gimp_viewable_get_size(), but make sure to set *width and *height anyway,
-   * so that the correct size is reported to the projection through
-   * gimp_projectable_get_size().  see issue #3134.
-   */
-  if (gimp_container_is_empty (private->children))
-    result = FALSE;
-
-  return result;
+  /*  ... otherwise, return "no content"  */
+  return FALSE;
 }
 
 static GimpContainer *
@@ -1341,6 +1330,30 @@ gimp_group_layer_get_format (GimpProjectable *projectable)
   return get_projection_format (projectable, base_type, precision);
 }
 
+static GeglRectangle
+gimp_group_layer_get_bounding_box (GimpProjectable *projectable)
+{
+  GimpGroupLayerPrivate *private = GET_PRIVATE (projectable);
+  GeglRectangle          bounding_box;
+
+  bounding_box.x = 0;
+  bounding_box.y = 0;
+
+  if (private->reallocate_width  != 0 &&
+      private->reallocate_height != 0)
+    {
+      bounding_box.width  = private->reallocate_width;
+      bounding_box.height = private->reallocate_height;
+    }
+  else
+    {
+      bounding_box.width  = gimp_item_get_width  (GIMP_ITEM (projectable));
+      bounding_box.height = gimp_item_get_height (GIMP_ITEM (projectable));
+    }
+
+  return bounding_box;
+}
+
 static GeglNode *
 gimp_group_layer_get_graph (GimpProjectable *projectable)
 {
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 54759f76b2..30875ef3f2 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -194,6 +194,7 @@ static void
 
 static void        gimp_image_projectable_flush  (GimpProjectable   *projectable,
                                                   gboolean           invalidate_preview);
+static GeglRectangle gimp_image_get_bounding_box (GimpProjectable   *projectable);
 static GeglNode   * gimp_image_get_graph         (GimpProjectable   *projectable);
 static GimpImage  * gimp_image_get_image         (GimpProjectable   *projectable);
 static const Babl * gimp_image_get_proj_format   (GimpProjectable   *projectable);
@@ -678,7 +679,7 @@ gimp_projectable_iface_init (GimpProjectableInterface *iface)
   iface->flush              = gimp_image_projectable_flush;
   iface->get_image          = gimp_image_get_image;
   iface->get_format         = gimp_image_get_proj_format;
-  iface->get_size           = (void (*) (GimpProjectable*, gint*, gint*)) gimp_image_get_size;
+  iface->get_bounding_box   = gimp_image_get_bounding_box;
   iface->get_graph          = gimp_image_get_graph;
   iface->invalidate_preview = (void (*) (GimpProjectable*)) gimp_viewable_invalidate_preview;
 }
@@ -1493,6 +1494,16 @@ gimp_image_srgb_to_pixel (GimpPickable  *pickable,
                                           color, format, pixel);
 }
 
+static GeglRectangle
+gimp_image_get_bounding_box (GimpProjectable *projectable)
+{
+  GimpImage *image = GIMP_IMAGE (projectable);
+
+  return *GEGL_RECTANGLE (0, 0,
+                          gimp_image_get_width  (image),
+                          gimp_image_get_height (image));
+}
+
 static GeglNode *
 gimp_image_get_graph (GimpProjectable *projectable)
 {
diff --git a/app/core/gimpprojectable.c b/app/core/gimpprojectable.c
index e8ded97492..8814c98eb9 100644
--- a/app/core/gimpprojectable.c
+++ b/app/core/gimpprojectable.c
@@ -90,10 +90,8 @@ gimp_projectable_default_init (GimpProjectableInterface *iface)
                   G_SIGNAL_RUN_FIRST,
                   G_STRUCT_OFFSET (GimpProjectableInterface, bounds_changed),
                   NULL, NULL,
-                  gimp_marshal_VOID__INT_INT_INT_INT,
-                  G_TYPE_NONE, 4,
-                  G_TYPE_INT,
-                  G_TYPE_INT,
+                  gimp_marshal_VOID__INT_INT,
+                  G_TYPE_NONE, 2,
                   G_TYPE_INT,
                   G_TYPE_INT);
 }
@@ -135,14 +133,12 @@ gimp_projectable_structure_changed (GimpProjectable *projectable)
 void
 gimp_projectable_bounds_changed (GimpProjectable *projectable,
                                  gint             old_x,
-                                 gint             old_y,
-                                 gint             old_width,
-                                 gint             old_height)
+                                 gint             old_y)
 {
   g_return_if_fail (GIMP_IS_PROJECTABLE (projectable));
 
   g_signal_emit (projectable, projectable_signals[BOUNDS_CHANGED], 0,
-                 old_x, old_y, old_width, old_height);
+                 old_x, old_y);
 }
 
 GimpImage *
@@ -195,24 +191,20 @@ gimp_projectable_get_offset (GimpProjectable *projectable,
     iface->get_offset (projectable, x, y);
 }
 
-void
-gimp_projectable_get_size (GimpProjectable *projectable,
-                           gint            *width,
-                           gint            *height)
+GeglRectangle
+gimp_projectable_get_bounding_box (GimpProjectable *projectable)
 {
   GimpProjectableInterface *iface;
+  GeglRectangle             result = {};
 
-  g_return_if_fail (GIMP_IS_PROJECTABLE (projectable));
-  g_return_if_fail (width  != NULL);
-  g_return_if_fail (height != NULL);
+  g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), result);
 
   iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable);
 
-  *width  = 0;
-  *height = 0;
+  if (iface->get_bounding_box)
+    result = iface->get_bounding_box (projectable);
 
-  if (iface->get_size)
-    iface->get_size (projectable, width, height);
+  return result;
 }
 
 GeglNode *
diff --git a/app/core/gimpprojectable.h b/app/core/gimpprojectable.h
index 836017dabd..955f3a4cb8 100644
--- a/app/core/gimpprojectable.h
+++ b/app/core/gimpprojectable.h
@@ -45,9 +45,7 @@ struct _GimpProjectableInterface
   void         (* structure_changed)  (GimpProjectable *projectable);
   void         (* bounds_changed)     (GimpProjectable *projectable,
                                        gint             old_x,
-                                       gint             old_y,
-                                       gint             old_width,
-                                       gint             old_height);
+                                       gint             old_y);
 
   /*  virtual functions  */
   GimpImage  * (* get_image)          (GimpProjectable *projectable);
@@ -55,9 +53,7 @@ struct _GimpProjectableInterface
   void         (* get_offset)         (GimpProjectable *projectable,
                                        gint            *x,
                                        gint            *y);
-  void         (* get_size)           (GimpProjectable *projectable,
-                                       gint            *width,
-                                       gint            *height);
+  GeglRectangle (* get_bounding_box)  (GimpProjectable *projectable);
   GeglNode   * (* get_graph)          (GimpProjectable *projectable);
   void         (* begin_render)       (GimpProjectable *projectable);
   void         (* end_render)         (GimpProjectable *projectable);
@@ -77,18 +73,14 @@ void         gimp_projectable_flush              (GimpProjectable *projectable,
 void         gimp_projectable_structure_changed  (GimpProjectable *projectable);
 void         gimp_projectable_bounds_changed     (GimpProjectable *projectable,
                                                   gint             old_x,
-                                                  gint             old_y,
-                                                  gint             old_width,
-                                                  gint             old_height);
+                                                  gint             old_y);
 
 GimpImage  * gimp_projectable_get_image          (GimpProjectable *projectable);
 const Babl * gimp_projectable_get_format         (GimpProjectable *projectable);
 void         gimp_projectable_get_offset         (GimpProjectable *projectable,
                                                   gint            *x,
                                                   gint            *y);
-void         gimp_projectable_get_size           (GimpProjectable *projectable,
-                                                  gint            *width,
-                                                  gint            *height);
+GeglRectangle gimp_projectable_get_bounding_box  (GimpProjectable *projectable);
 GeglNode   * gimp_projectable_get_graph          (GimpProjectable *projectable);
 void         gimp_projectable_begin_render       (GimpProjectable *projectable);
 void         gimp_projectable_end_render         (GimpProjectable *projectable);
diff --git a/app/core/gimpprojection.c b/app/core/gimpprojection.c
index 9f82833b57..8055d6bc7e 100644
--- a/app/core/gimpprojection.c
+++ b/app/core/gimpprojection.c
@@ -163,8 +163,6 @@ static void
 static void   gimp_projection_projectable_bounds_changed (GimpProjectable *projectable,
                                                           gint             old_x,
                                                           gint             old_y,
-                                                          gint             old_w,
-                                                          gint             old_h,
                                                           GimpProjection  *proj);
 
 
@@ -365,10 +363,10 @@ gimp_projection_get_buffer (GimpPickable *pickable)
 
   if (! proj->priv->buffer)
     {
-      gint width;
-      gint height;
+      GeglRectangle bounding_box;
 
-      gimp_projectable_get_size (proj->priv->projectable, &width, &height);
+      bounding_box =
+        gimp_projectable_get_bounding_box (proj->priv->projectable);
 
       gimp_projection_allocate_buffer (proj);
 
@@ -379,7 +377,9 @@ gimp_projection_get_buffer (GimpPickable *pickable)
        *  image appear incrementally, but it keeps everything
        *  responsive.
        */
-      gimp_projection_add_update_area (proj, 0, 0, width, height);
+      gimp_projection_add_update_area (proj,
+                                       bounding_box.x,     bounding_box.y,
+                                       bounding_box.width, bounding_box.height);
       proj->priv->invalidate_preview = TRUE;
       gimp_projection_flush (proj);
     }
@@ -565,18 +565,17 @@ gimp_projection_finish_draw (GimpProjection *proj)
 static void
 gimp_projection_allocate_buffer (GimpProjection *proj)
 {
-  const Babl *format;
-  gint        width;
-  gint        height;
+  const Babl    *format;
+  GeglRectangle  bounding_box;
 
   if (proj->priv->buffer)
     return;
 
-  format = gimp_projection_get_format (GIMP_PICKABLE (proj));
-  gimp_projectable_get_size (proj->priv->projectable, &width, &height);
+  format       = gimp_projection_get_format (GIMP_PICKABLE (proj));
+  bounding_box =
+    gimp_projectable_get_bounding_box (proj->priv->projectable);
 
-  proj->priv->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
-                                        format);
+  proj->priv->buffer = gegl_buffer_new (&bounding_box, format);
 
   proj->priv->validate_handler =
     GIMP_TILE_HANDLER_VALIDATE (
@@ -613,9 +612,9 @@ gimp_projection_add_update_area (GimpProjection *proj,
                                  gint            h)
 {
   cairo_rectangle_int_t rect;
-  gint                  width, height;
+  GeglRectangle         bounding_box;
 
-  gimp_projectable_get_size   (proj->priv->projectable, &width, &height);
+  bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable);
 
   /*  align the rectangle to the UPDATE_CHUNK_WIDTH x UPDATE_CHUNK_HEIGHT grid,
    *  to decrease the complexity of the update area.
@@ -628,9 +627,8 @@ gimp_projection_add_update_area (GimpProjection *proj,
   w -= x;
   h -= y;
 
-  if (gimp_rectangle_intersect (x, y, w, h,
-                                0, 0, width, height,
-                                &rect.x, &rect.y, &rect.width, &rect.height))
+  if (gegl_rectangle_intersect ((GeglRectangle *) &rect,
+                                GEGL_RECTANGLE (x, y, w, h), &bounding_box))
     {
       if (proj->priv->update_region)
         cairo_region_union_rectangle (proj->priv->update_region, &rect);
@@ -695,13 +693,13 @@ gimp_projection_update_priority_rect (GimpProjection *proj)
   if (proj->priv->iter)
     {
       GeglRectangle rect;
+      GeglRectangle bounding_box;
       gint          off_x, off_y;
-      gint          width, height;
 
       rect = proj->priv->priority_rect;
 
       gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y);
-      gimp_projectable_get_size   (proj->priv->projectable, &width, &height);
+      bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable);
 
       /*  subtract the projectable's offsets because the list of update
        *  areas is in tile-pyramid coordinates, but our external API is
@@ -710,9 +708,7 @@ gimp_projection_update_priority_rect (GimpProjection *proj)
       rect.x -= off_x;
       rect.y -= off_y;
 
-      gegl_rectangle_intersect (&rect,
-                                &rect,
-                                GEGL_RECTANGLE (0, 0, width, height));
+      gegl_rectangle_intersect (&rect, &rect, &bounding_box);
 
       gimp_chunk_iterator_set_priority_rect (proj->priv->iter, &rect);
     }
@@ -880,29 +876,29 @@ gimp_projection_paint_area (GimpProjection *proj,
                             gint            w,
                             gint            h)
 {
-  gint off_x, off_y;
-  gint width, height;
+  gint          off_x, off_y;
+  GeglRectangle bounding_box;
+  GeglRectangle rect;
 
   gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y);
-  gimp_projectable_get_size   (proj->priv->projectable, &width, &height);
+  bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable);
 
-  if (gimp_rectangle_intersect (x, y, w, h,
-                                0, 0, width, height,
-                                &x, &y, &w, &h))
+  if (gegl_rectangle_intersect (&rect,
+                                GEGL_RECTANGLE (x, y, w, h), &bounding_box))
     {
       if (now)
         {
           gimp_tile_handler_validate_validate (
             proj->priv->validate_handler,
             proj->priv->buffer,
-            GEGL_RECTANGLE (x, y, w, h),
+            &rect,
             FALSE);
         }
       else
         {
           gimp_tile_handler_validate_invalidate (
             proj->priv->validate_handler,
-            GEGL_RECTANGLE (x, y, w, h));
+            &rect);
         }
 
       /*  add the projectable's offsets because the list of update areas
@@ -911,10 +907,10 @@ gimp_projection_paint_area (GimpProjection *proj,
        */
       g_signal_emit (proj, projection_signals[UPDATE], 0,
                      now,
-                     x + off_x,
-                     y + off_y,
-                     w,
-                     h);
+                     rect.x + off_x,
+                     rect.y + off_y,
+                     rect.width,
+                     rect.height);
     }
 }
 
@@ -958,26 +954,31 @@ static void
 gimp_projection_projectable_structure_changed (GimpProjectable *projectable,
                                                GimpProjection  *proj)
 {
-  gint width, height;
+  GeglRectangle bounding_box;
 
   gimp_projection_free_buffer (proj);
 
-  gimp_projectable_get_size (projectable, &width, &height);
+  bounding_box = gimp_projectable_get_bounding_box (projectable);
 
-  gimp_projection_add_update_area (proj, 0, 0, width, height);
+  gimp_projection_add_update_area (proj,
+                                   bounding_box.x,     bounding_box.y,
+                                   bounding_box.width, bounding_box.height);
 }
 
 static void
 gimp_projection_projectable_bounds_changed (GimpProjectable *projectable,
                                             gint             old_x,
                                             gint             old_y,
-                                            gint             old_w,
-                                            gint             old_h,
                                             GimpProjection  *proj)
 {
   GeglBuffer              *old_buffer = proj->priv->buffer;
   GimpTileHandlerValidate *old_validate_handler;
-  gint                     x, y, w, h;
+  GeglRectangle            old_bounding_box;
+  GeglRectangle            bounding_box;
+  GeglRectangle            old_bounds;
+  GeglRectangle            bounds;
+  GeglRectangle            int_bounds;
+  gint                     x, y;
   gint                     dx, dy;
 
   if (! old_buffer)
@@ -987,23 +988,34 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable,
       return;
     }
 
+  old_bounding_box = *gegl_buffer_get_extent (old_buffer);
+
   gimp_projectable_get_offset (projectable, &x, &y);
-  gimp_projectable_get_size   (projectable, &w, &h);
+  bounding_box = gimp_projectable_get_bounding_box (projectable);
 
-  if (x == old_x && y == old_y && w == old_w && h == old_h)
-    return;
+  if (x == old_x && y == old_y &&
+      gegl_rectangle_equal (&bounding_box, &old_bounding_box))
+    {
+      return;
+    }
+
+  old_bounds    = old_bounding_box;
+  old_bounds.x += old_x;
+  old_bounds.y += old_y;
 
-  if (! gimp_rectangle_intersect (x,     y,     w,     h,
-                                  old_x, old_y, old_w, old_h,
-                                  NULL,  NULL,  NULL,  NULL))
+  bounds        = bounding_box;
+  bounds.x     += x;
+  bounds.y     += y;
+
+  if (! gegl_rectangle_intersect (&int_bounds, &bounds, &old_bounds))
     {
       gimp_projection_projectable_structure_changed (projectable, proj);
 
       return;
     }
 
-  dx = old_x - x;
-  dy = old_y - y;
+  dx = x - old_x;
+  dy = y - old_y;
 
 #if 1
   /* FIXME:  when there's an offset between the new bounds and the old bounds,
@@ -1038,9 +1050,15 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable,
   gimp_projection_allocate_buffer (proj);
 
   gimp_tile_handler_validate_buffer_copy (old_buffer,
-                                          GEGL_RECTANGLE (0, 0, old_w, old_h),
+                                          GEGL_RECTANGLE (int_bounds.x - old_x,
+                                                          int_bounds.y - old_y,
+                                                          int_bounds.width,
+                                                          int_bounds.height),
                                           proj->priv->buffer,
-                                          GEGL_RECTANGLE (dx, dy, old_w, old_h));
+                                          GEGL_RECTANGLE (int_bounds.x - x,
+                                                          int_bounds.y - y,
+                                                          int_bounds.width,
+                                                          int_bounds.height));
 
   if (old_validate_handler)
     {
@@ -1053,20 +1071,49 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable,
 
   if (proj->priv->update_region)
     {
-      const cairo_rectangle_int_t bounds = {0, 0, w, h};
-
-      cairo_region_translate           (proj->priv->update_region, dx, dy);
-      cairo_region_intersect_rectangle (proj->priv->update_region, &bounds);
+      cairo_region_translate (proj->priv->update_region, dx, dy);
+      cairo_region_intersect_rectangle (
+        proj->priv->update_region,
+        (const cairo_rectangle_int_t *) &bounding_box);
     }
 
-  if (dx > 0)
-    gimp_projection_add_update_area (proj, 0, 0, dx, h);
-  if (dy > 0)
-    gimp_projection_add_update_area (proj, 0, 0, w, dy);
-  if (dx + old_w < w)
-    gimp_projection_add_update_area (proj, dx + old_w, 0, w - (dx + old_w), h);
-  if (dy + old_h < h)
-    gimp_projection_add_update_area (proj, 0, dy + old_h, w, h - (dy + old_h));
+  int_bounds.x -= x;
+  int_bounds.y -= y;
+
+  if (int_bounds.x > bounding_box.x)
+    {
+      gimp_projection_add_update_area (proj,
+                                       bounding_box.x,
+                                       bounding_box.y,
+                                       int_bounds.x - bounding_box.x,
+                                       bounding_box.height);
+    }
+  if (int_bounds.y > bounding_box.y)
+    {
+      gimp_projection_add_update_area (proj,
+                                       bounding_box.x,
+                                       bounding_box.y,
+                                       bounding_box.width,
+                                       int_bounds.y - bounding_box.y);
+    }
+  if (int_bounds.x + int_bounds.width < bounding_box.x + bounding_box.width)
+    {
+      gimp_projection_add_update_area (proj,
+                                       int_bounds.x + int_bounds.width,
+                                       bounding_box.y,
+                                       bounding_box.x + bounding_box.width -
+                                       (int_bounds.x  + int_bounds.width),
+                                       bounding_box.height);
+    }
+  if (int_bounds.y + int_bounds.height < bounding_box.y + bounding_box.height)
+    {
+      gimp_projection_add_update_area (proj,
+                                       bounding_box.x,
+                                       int_bounds.y + int_bounds.height,
+                                       bounding_box.width,
+                                       bounding_box.y + bounding_box.height -
+                                       (int_bounds.y  + int_bounds.height));
+    }
 
   proj->priv->invalidate_preview = TRUE;
 }


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