[gimp] app: improve alpha to selection with multiple items.



commit 32740ac0decc821436e443fb6a687564fa95538b
Author: Jehan <jehan girinstud io>
Date:   Wed May 20 18:16:30 2020 +0200

    app: improve alpha to selection with multiple items.
    
    I created a new function gimp_channel_combine_items() which combines a
    list of items with a channel. The list of items is first combined
    together as an union set, then only combined with the channel with the
    desired operation (this is important for operations such as intersect
    which was broken in my previous commit because all items would be
    intersected with each other and the selection, whereas we actually want
    the union of all items to be intersected with the selection). This new
    function is now used for "Alpha to Selection".
    
    Also similarly to copy or color-pick on multi-layers, alpha to selection
    will now use the composited result of the multi-layers as visible. In
    particular it means that opacity, modes and visible properties on the
    layers are taken into account. Alpha to selection on a single layer
    though still works as previously, only taking the non-composited layer
    data into account.
    I am actually struggling if alpha to selection on uncomposited layers
    (just an union on alpha to selection result for each layer) would not
    make sense to on some workflows. To be experimented.
    
    Finally it is to be noted that this function should also work on
    channels and vectors (both single or multiple; and of course in such
    cases, compositing does not matter) though I haven't tested yet. It
    could even work with a source mix of layers, channels and vectors,
    though our GUI does not allow such action currently.

 app/actions/layers-commands.c  |  16 ++----
 app/core/gimpchannel-combine.c | 110 +++++++++++++++++++++++++++++++++++++++++
 app/core/gimpchannel-combine.h |   4 ++
 3 files changed, 119 insertions(+), 11 deletions(-)
---
diff --git a/app/actions/layers-commands.c b/app/actions/layers-commands.c
index ee67147f05..6b8be0922f 100644
--- a/app/actions/layers-commands.c
+++ b/app/actions/layers-commands.c
@@ -35,6 +35,7 @@
 
 #include "core/gimp.h"
 #include "core/gimpchannel.h"
+#include "core/gimpchannel-combine.h"
 #include "core/gimpcontainer.h"
 #include "core/gimpcontext.h"
 #include "core/gimpdrawable-fill.h"
@@ -1556,22 +1557,15 @@ layers_alpha_to_selection_cmd_callback (GimpAction *action,
 {
   GimpImage      *image;
   GList          *layers;
-  GList          *iter;
   GimpChannelOps  operation;
   return_if_no_layers (image, layers, data);
 
   operation = (GimpChannelOps) g_variant_get_int32 (value);
 
-  for (iter = layers; iter; iter = iter->next)
-    {
-      if (operation != GIMP_CHANNEL_OP_REPLACE || iter == layers)
-        gimp_item_to_selection (GIMP_ITEM (iter->data), operation,
-                                TRUE, FALSE, 0.0, 0.0);
-      else
-         gimp_item_to_selection (GIMP_ITEM (iter->data),
-                                 GIMP_CHANNEL_OP_ADD,
-                                 TRUE, FALSE, 0.0, 0.0);
-    }
+  gimp_channel_push_undo (gimp_image_get_mask (image),
+                          C_("undo-type", "Alpha to Selection"));
+  gimp_channel_combine_items (gimp_image_get_mask (image),
+                              layers, operation);
   gimp_image_flush (image);
 }
 
diff --git a/app/core/gimpchannel-combine.c b/app/core/gimpchannel-combine.c
index 8326f65b0c..2d87078f57 100644
--- a/app/core/gimpchannel-combine.c
+++ b/app/core/gimpchannel-combine.c
@@ -29,8 +29,15 @@
 
 #include "gegl/gimp-gegl-mask-combine.h"
 
+#include "gimp.h"
 #include "gimpchannel.h"
 #include "gimpchannel-combine.h"
+#include "gimpimage.h"
+#include "gimpimage-merge.h"
+#include "gimpimage-new.h"
+#include "gimplayer.h"
+
+#include "vectors/gimpvectors.h"
 
 
 typedef struct
@@ -469,3 +476,106 @@ gimp_channel_combine_buffer (GimpChannel    *mask,
 
   gimp_channel_combine_end (mask, &data);
 }
+
+/**
+ * gimp_channel_combine_items:
+ * @channel:
+ * @items:
+ * @op:
+ *
+ * Edit @channel with the given @items, according to the boolean @op.
+ * @items need to belong to the same image, but it doesn't have to be
+ * the owner image of @channel.
+ * If @items contain a single layer, it will be used as-is, without
+ * caring about opacity, mode or visibility.
+ * If @items contain several layers, they will be used composited using
+ * their opacity, mode, visibility, etc. properties within the image
+ * (similar as if a "merge visible" had been applied to the image then
+ * the resulting layer used alone).
+ * If @items contain channels or vectors, they will be added as a set
+ * (i.e. as a single item which is an union of other items). E.g. an
+ * combine in GIMP_CHANNEL_OP_INTERSECT mode does not intersect all
+ * @items with each other and @channel. It first adds-alike all @items
+ * together, then intersects the result with @channel.
+ */
+void
+gimp_channel_combine_items (GimpChannel    *mask,
+                            GList          *items,
+                            GimpChannelOps  op)
+{
+  GimpImage   *image;
+  GimpImage   *items_image = NULL;
+  GimpImage   *temp_image  = NULL;
+
+  GimpChannel *channel     = NULL;
+  GList       *layers      = NULL;
+  GList       *iter;
+
+  g_return_if_fail (GIMP_IS_CHANNEL (mask));
+  g_return_if_fail (g_list_length (items) > 0);
+
+  for (iter = items; iter; iter = iter->next)
+    {
+      g_return_if_fail (GIMP_IS_LAYER (iter->data)   ||
+                        GIMP_IS_CHANNEL (iter->data) ||
+                        GIMP_IS_VECTORS (iter->data));
+
+      if (items_image == NULL)
+        items_image = gimp_item_get_image (iter->data);
+      else
+        g_return_if_fail (items_image == gimp_item_get_image (iter->data));
+
+      if (GIMP_IS_LAYER (iter->data))
+        layers = g_list_prepend (layers, iter->data);
+    }
+
+  image = gimp_item_get_image (GIMP_ITEM (mask));
+  if (g_list_length (layers) > 1)
+    {
+      GList *merged_layers;
+
+      temp_image = gimp_image_new_from_drawables (image->gimp, items, FALSE);
+      merged_layers = gimp_image_merge_visible_layers (temp_image,
+                                                       gimp_get_user_context (temp_image->gimp),
+                                                       GIMP_CLIP_TO_IMAGE,
+                                                       FALSE, TRUE, NULL);
+      g_return_if_fail (g_list_length (merged_layers) == 1);
+      gimp_item_to_selection (GIMP_ITEM (merged_layers->data),
+                              GIMP_CHANNEL_OP_REPLACE,
+                              TRUE, FALSE, 0.0, 0.0);
+      channel = gimp_image_get_mask (temp_image);
+    }
+
+  if (! channel)
+    {
+      channel = gimp_channel_new (image,
+                                      gimp_image_get_width (image),
+                                      gimp_image_get_height (image),
+                                      NULL, NULL);
+      gimp_channel_clear (channel, NULL, FALSE);
+
+      if (g_list_length (layers) == 1)
+        gimp_channel_combine_buffer (channel,
+                                     gimp_drawable_get_buffer (GIMP_DRAWABLE (layers->data)),
+                                     GIMP_CHANNEL_OP_REPLACE, 0, 0);
+    }
+
+  for (iter = items; iter; iter = iter->next)
+    {
+      if (! GIMP_IS_LAYER (iter->data))
+        gimp_channel_combine_buffer (channel,
+                                     gimp_drawable_get_buffer (GIMP_DRAWABLE (iter->data)),
+                                     GIMP_CHANNEL_OP_ADD, 0, 0);
+    }
+
+  gimp_channel_combine_buffer (mask,
+                               gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)),
+                               op, 0, 0);
+
+  if (temp_image)
+    g_object_unref (temp_image);
+  else
+    g_object_unref (channel);
+
+  g_list_free (layers);
+}
diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h
index de1f125c5c..fdee026745 100644
--- a/app/core/gimpchannel-combine.h
+++ b/app/core/gimpchannel-combine.h
@@ -52,5 +52,9 @@ void   gimp_channel_combine_buffer       (GimpChannel    *mask,
                                           gint            off_x,
                                           gint            off_y);
 
+void   gimp_channel_combine_items        (GimpChannel    *mask,
+                                          GList          *items,
+                                          GimpChannelOps  op);
+
 
 #endif /* __GIMP_CHANNEL_COMBINE_H__ */


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