Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gimpimage-merge.c   Sprache: C

 

/* 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 <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"

#include "core-types.h"

#include "gegl/gimp-babl-compat.h"
#include "gegl/gimp-gegl-apply-operation.h"
#include "gegl/gimp-gegl-nodes.h"
#include "gegl/gimp-gegl-utils.h"

#include "path/gimppath.h"

#include "gimp.h"
#include "gimpcontext.h"
#include "gimpdrawable-filters.h"
#include "gimperror.h"
#include "gimpgrouplayer.h"
#include "gimpimage.h"
#include "gimpimage-merge.h"
#include "gimpimage-undo.h"
#include "gimpitemstack.h"
#include "gimplayer-floating-selection.h"
#include "gimplayer-new.h"
#include "gimplayermask.h"
#include "gimpparasitelist.h"
#include "gimppickable.h"
#include "gimpprogress.h"
#include "gimpprojectable.h"
#include "gimpundostack.h"

#include "gimp-intl.h"


static GSList    * gimp_image_trim_merge_list (GimpImage     *image,
                                               GSList        *merge_list);
static GimpLayer * gimp_image_merge_layers    (GimpImage     *image,
                                               GimpContainer *container,
                                               GSList        *merge_list,
                                               GimpContext   *context,
                                               GimpMergeType  merge_type,
                                               const gchar   *undo_desc,
                                               GimpProgress  *progress);


/*  public functions  */

GList *
gimp_image_merge_visible_layers (GimpImage     *image,
                                 GimpContext   *context,
                                 GimpMergeType  merge_type,
                                 gboolean       merge_active_group,
                                 gboolean       discard_invisible,
                                 GimpProgress  *progress)
{
  const gchar *undo_desc  = C_("undo-type", "Merge Visible Layers");
  GList       *containers = NULL;
  GList       *new_layers = NULL;
  GList       *iter;
  GList       *iter2;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);

  if (merge_active_group)
    {
      GList *selected_layers = gimp_image_get_selected_layers (image);

      /*  if the active layer is the floating selection, get the
       *  underlying drawable, but only if it is a layer
       */

      if (g_list_length (selected_layers) == 1 && gimp_layer_is_floating_sel (selected_layers->data))
        {
          GimpDrawable *fs_drawable;

          fs_drawable = gimp_layer_get_floating_sel_drawable (selected_layers->data);

          if (GIMP_IS_LAYER (fs_drawable))
            containers = g_list_prepend (containers,
                                         gimp_item_get_container (GIMP_ITEM (fs_drawable)));
        }
      else
        {
          for (iter = selected_layers; iter; iter = iter->next)
            if (! gimp_item_get_parent (iter->data))
              break;

          /* No need to list selected groups if any selected layer is
           * top-level.
           */

          if (iter == NULL)
            {
              for (iter = selected_layers; iter; iter = iter->next)
                {
                  for (iter2 = selected_layers; iter2; iter2 = iter2->next)
                    {
                      /* Only retain a selected layer's container if no
                       * other selected layers are its parents.
                       */

                      if (iter->data != iter2->data &&
                          gimp_viewable_is_ancestor (iter2->data, iter->data))
                        break;
                    }
                  if (iter2 == NULL &&
                      ! g_list_find (containers, gimp_item_get_container (GIMP_ITEM (iter->data))))
                    containers = g_list_prepend (containers,
                                                 gimp_item_get_container (GIMP_ITEM (iter->data)));
                }
            }
        }
    }
  if (! containers)
    containers = g_list_prepend (NULL, gimp_image_get_layers (image));

  gimp_set_busy (image->gimp);
  gimp_image_undo_group_start (image,
                               GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
                               undo_desc);

  for (iter = containers; iter; iter = iter->next)
    {
      GimpContainer *container      = iter->data;
      GSList        *merge_list     = NULL;
      GSList        *invisible_list = NULL;

      for (iter2 = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (container));
           iter2;
           iter2 = g_list_next (iter2))
        {
          GimpLayer *layer = iter2->data;

          if (gimp_layer_is_floating_sel (layer))
            continue;

          if (gimp_item_get_visible (GIMP_ITEM (layer)))
            {
              merge_list = g_slist_append (merge_list, layer);
            }
          else if (discard_invisible)
            {
              invisible_list = g_slist_append (invisible_list, layer);
            }
        }

      if (merge_list)
        {
          GimpLayer *layer;

          /* if there's a floating selection, anchor it */
          if (gimp_image_get_floating_selection (image))
            floating_sel_anchor (gimp_image_get_floating_selection (image));

          layer = gimp_image_merge_layers (image,
                                           container,
                                           merge_list, context, merge_type,
                                           undo_desc, progress);
          g_slist_free (merge_list);

          if (invisible_list)
            {
              GSList *list;

              for (list = invisible_list; list; list = g_slist_next (list))
                gimp_image_remove_layer (image, list->data, TRUE, NULL);

              g_slist_free (invisible_list);
            }

          new_layers = g_list_prepend (new_layers, layer);
        }
    }

  gimp_image_set_selected_layers (image, new_layers);
  gimp_image_undo_group_end (image);
  gimp_unset_busy (image->gimp);

  g_list_free (new_layers);
  g_list_free (containers);

  return gimp_image_get_selected_layers (image);
}

GimpLayer *
gimp_image_flatten (GimpImage     *image,
                    GimpContext   *context,
                    GimpProgress  *progress,
                    GError       **error)
{
  GList     *list;
  GSList    *merge_list = NULL;
  GimpLayer *layer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  for (list = gimp_image_get_layer_iter (image);
       list;
       list = g_list_next (list))
    {
      layer = list->data;

      if (gimp_layer_is_floating_sel (layer))
        continue;

      if (gimp_item_get_visible (GIMP_ITEM (layer)))
        merge_list = g_slist_append (merge_list, layer);
    }

  if (merge_list)
    {
      const gchar *undo_desc = C_("undo-type", "Flatten Image");

      gimp_set_busy (image->gimp);

      gimp_image_undo_group_start (image,
                                   GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
                                   undo_desc);

      /* if there's a floating selection, anchor it */
      if (gimp_image_get_floating_selection (image))
        floating_sel_anchor (gimp_image_get_floating_selection (image));

      layer = gimp_image_merge_layers (image,
                                       gimp_image_get_layers (image),
                                       merge_list, context,
                                       GIMP_FLATTEN_IMAGE,
                                       undo_desc, progress);
      g_slist_free (merge_list);

      gimp_image_alpha_changed (image);

      gimp_image_undo_group_end (image);

      gimp_unset_busy (image->gimp);

      return layer;
    }

  g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                       _("Cannot flatten an image without any visible layer."));
  return NULL;
}

GList *
gimp_image_merge_down (GimpImage      *image,
                       GList          *layers,
                       GimpContext    *context,
                       GimpMergeType   merge_type,
                       GimpProgress   *progress,
                       GimpItem      **blink_item,
                       GError        **error)
{
  GList       *merged_layers = NULL;
  GList       *merge_lists   = NULL;
  GSList      *merge_list    = NULL;
  GimpLayer   *layer;
  GList       *list;
  const gchar *undo_desc;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  layers = g_list_copy (layers);
  while ((list = layers))
    {
      GList *list2;

      g_return_val_if_fail (GIMP_IS_LAYER (list->data), NULL);
      g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (list->data)), NULL);

      if (gimp_layer_is_floating_sel (list->data))
        {
          g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                               _("Cannot merge down a floating selection."));
          if (blink_item)
            *blink_item = GIMP_ITEM (list->data);
          g_list_free (layers);
          g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
          return NULL;
        }
      if (gimp_layer_get_mode (list->data) == GIMP_LAYER_MODE_PASS_THROUGH)
        {
          g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                               _("Cannot merge down a pass through layer group."));
          if (blink_item)
            *blink_item = GIMP_ITEM (list->data);
          g_list_free (layers);
          g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
          return NULL;
        }

      if (! gimp_item_get_visible (GIMP_ITEM (list->data)))
        {
          g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                               _("Cannot merge down an invisible layer."));
          if (blink_item)
            *blink_item = GIMP_ITEM (list->data);
          g_list_free (layers);
          g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
          return NULL;
        }

      for (list2 = gimp_item_get_container_iter (GIMP_ITEM (list->data));
           list2;
           list2 = g_list_next (list2))
        {
          if (list2->data == list->data)
            break;
        }

      layer = NULL;
      for (list2 = g_list_next (list2);
           list2;
           list2 = g_list_next (list2))
        {
          layer = list2->data;

          if (gimp_item_get_visible (GIMP_ITEM (layer)))
            {
              if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
                {
                  g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                                       _("Cannot merge down to a layer group."));
                  if (blink_item)
                    *blink_item = GIMP_ITEM (layer);
                  g_list_free (layers);
                  g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
                  return NULL;
                }

              if (gimp_item_is_content_locked (GIMP_ITEM (layer), NULL))
                {
                  g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                                       _("The layer to merge down to is locked."));
                  if (blink_item)
                    *blink_item = GIMP_ITEM (layer);
                  g_list_free (layers);
                  g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
                  return NULL;
                }

              break;
            }

          layer = NULL;
        }

      if (! layer)
        {
          g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                               _("There is no visible layer to merge down to."));
          g_list_free (layers);
          g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);
          return NULL;
        }

      merge_list = g_slist_append (merge_list, list->data);
      layers     = g_list_delete_link (layers, layers);

      if ((list = g_list_find (layers, layer)))
        {
          /* The next item is itself in the merge down list. Continue
           * filling the same merge list instead of starting a new one.
           */

          layers = g_list_delete_link (layers, list);
          layers = g_list_prepend (layers, layer);

          continue;
        }

      merge_list  = g_slist_append (merge_list, layer);
      merge_lists = g_list_prepend (merge_lists, merge_list);
      merge_list  = NULL;
    }

  undo_desc = C_("undo-type", "Merge Down");

  gimp_set_busy (image->gimp);

  gimp_image_undo_group_start (image,
                               GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
                               undo_desc);

  for (list = merge_lists; list; list = list->next)
    {
      merge_list = list->data;
      layer = gimp_image_merge_layers (image,
                                       gimp_item_get_container (merge_list->data),
                                       merge_list, context, merge_type,
                                       undo_desc, progress);
      merged_layers = g_list_prepend (merged_layers, layer);
    }

  g_list_free_full (merge_lists, (GDestroyNotify) g_slist_free);

  gimp_image_undo_group_end (image);

  gimp_unset_busy (image->gimp);

  return merged_layers;
}

GimpLayer *
gimp_image_merge_group_layer (GimpImage      *image,
                              GimpGroupLayer *group)
{
  GimpLayer     *parent;
  GimpLayer     *layer;
  GeglBuffer    *pass_through_buffer = NULL;
  gboolean       is_pass_through     = FALSE;
  gint           index;
  GeglRectangle  rect;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL);
  g_return_val_if_fail (gimp_item_get_image (GIMP_ITEM (group)) == image, NULL);

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
                               C_("undo-type", "Merge Layer Group"));

  parent = gimp_layer_get_parent (GIMP_LAYER (group));
  index  = gimp_item_get_index (GIMP_ITEM (group));

  is_pass_through = (gimp_layer_get_mode (GIMP_LAYER (group)) == GIMP_LAYER_MODE_PASS_THROUGH &&
                     gimp_item_get_visible (GIMP_ITEM (group)));
  if (is_pass_through &&
      (gimp_layer_get_opacity (GIMP_LAYER (group)) < 1.0 ||
       ! gimp_drawable_has_visible_filters (GIMP_DRAWABLE (group))))
    {
      GimpDrawable  *drawable  = GIMP_DRAWABLE (group);
      GeglNode      *mode_node = gimp_drawable_get_mode_node (drawable);

      rect = gegl_node_get_bounding_box (mode_node);
      pass_through_buffer = gegl_buffer_new (&rect, gimp_drawable_get_format (drawable));
      gegl_node_blit_buffer (mode_node, pass_through_buffer, &rect, 0, GEGL_ABYSS_NONE);
    }

  /* Merge down filter effects */
  gimp_drawable_merge_filters (GIMP_DRAWABLE (group));
  gimp_drawable_clear_filters (GIMP_DRAWABLE (group));

  layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (group),
                                           GIMP_TYPE_LAYER));

  gimp_object_set_name (GIMP_OBJECT (layer), gimp_object_get_name (group));

  gimp_image_remove_layer (image, GIMP_LAYER (group), TRUE, NULL);
  gimp_image_add_layer (image, layer, parent, index, TRUE);

  /* Pass-through groups are a very special case. The duplicate works
   * only if both these points are true:
   * - The group is at full opacity: with lower opacity, what we want is
   *   in fact the output of the "gimp:pass-through" mode (similar to
   *   "gimp:replace") because we can't reproduce the same render
   *   otherwise. This works well, since anyway the merged layer is
   *   ensured to be the bottomest one on its own level.
   * - The group has filters: gimp_drawable_merge_filters() will
   *   actually set the end-rendering to the drawable (kinda rasterizing
   *   the group layer).
   */

   if (pass_through_buffer)
    {
      /* The group duplicate may be smaller than actual render in case
       * of pass-through groups.
       */

      gimp_item_set_size (GIMP_ITEM (layer), rect.width, rect.height);

      if (rect.x != 0 || rect.y != 0)
        {
          GeglBuffer *buffer;

          buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
                                    gimp_drawable_get_format (GIMP_DRAWABLE (layer)));
          gegl_buffer_copy (pass_through_buffer, &rect, GEGL_ABYSS_NONE,
                            buffer, GEGL_RECTANGLE (0, 0, rect.width, rect.height));
          g_object_unref (pass_through_buffer);
          pass_through_buffer = buffer;
        }
      gimp_drawable_set_buffer_full (GIMP_DRAWABLE (layer), FALSE, NULL,
                                     pass_through_buffer, &rect, TRUE);
      g_object_unref (pass_through_buffer);
    }

  /* For pass-through group layers, we must remove all "big sister"
   * layers, i.e. all layers on the same level below the newly merged
   * layer, because their render is already integrated in the merged
   * layer. Therefore keeping them would change the whole image's
   * rendering.
   */

  if (is_pass_through)
    {
      GimpContainer *stack;
      GList         *iter;
      GList         *new_selected = g_list_prepend (NULL, layer);
      GList         *to_remove    = NULL;
      gboolean       remove       = FALSE;

      if (parent)
        stack = gimp_viewable_get_children (GIMP_VIEWABLE (parent));
      else
        stack = gimp_image_get_layers (image);

      for (iter = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (stack)); iter; iter = iter->next)
        {
          if (iter->data == layer)
            remove = TRUE;
          else if (remove && gimp_item_get_visible (iter->data))
            to_remove = g_list_prepend (to_remove, iter->data);
        }

      for (iter = to_remove; iter; iter = iter->next)
        gimp_image_remove_layer (image, GIMP_LAYER (iter->data), TRUE, new_selected);

      g_list_free (new_selected);
    }

  gimp_image_undo_group_end (image);

  return layer;
}


/* merging paths */

GimpPath *
gimp_image_merge_visible_paths (GimpImage  *image,
                                GError    **error)
{
  GList       *list;
  GList       *merge_list = NULL;
  GimpPath    *path;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  for (list = gimp_image_get_path_iter (image);
       list;
       list = g_list_next (list))
    {
      path = list->data;

      if (gimp_item_get_visible (GIMP_ITEM (path)))
        merge_list = g_list_prepend (merge_list, path);
    }

  merge_list = g_list_reverse (merge_list);

  if (merge_list && merge_list->next)
    {
      GimpPath *target_path;
      gchar    *name;
      gint      pos;

      gimp_set_busy (image->gimp);

      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_PATHS_MERGE,
                                   C_("undo-type", "Merge Visible Paths"));

      path = GIMP_PATH (merge_list->data);

      name = g_strdup (gimp_object_get_name (path));
      pos = gimp_item_get_index (GIMP_ITEM (path));

      target_path = GIMP_PATH (gimp_item_duplicate (GIMP_ITEM (path),
                                                    GIMP_TYPE_PATH));
      gimp_image_remove_path (image, path, TRUE, NULL);

      for (list = g_list_next (merge_list);
           list;
           list = g_list_next (list))
        {
          path = list->data;

          gimp_path_add_strokes (path, target_path);
          gimp_image_remove_path (image, path, TRUE, NULL);
        }

      gimp_object_take_name (GIMP_OBJECT (target_path), name);

      g_list_free (merge_list);

      /* FIXME tree */
      gimp_image_add_path (image, target_path, NULL, pos, TRUE);
      gimp_unset_busy (image->gimp);

      gimp_image_undo_group_end (image);

      return target_path;
    }
  else
    {
      g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
                           _("Not enough visible paths for a merge. "
                             "There must be at least two."));
      return NULL;
    }
}


/*  private functions  */

static GSList *
gimp_image_trim_merge_list (GimpImage *image,
                            GSList    *merge_list)
{
  GSList *trimmed_list = NULL;
  GSList *pass_through = NULL;

  for (GSList *iter = merge_list; iter; iter = iter->next)
    {
      GimpLayer *layer  = iter->data;
      gboolean   ignore = FALSE;

      for (GSList *iter2 = pass_through; iter2; iter2 = iter2->next)
        {
          GimpLayer *pass_through_parent = gimp_layer_get_parent (iter2->data);
          GimpLayer *cousin              = layer;

          do
            {
              GimpLayer *cousin_parent = gimp_layer_get_parent (cousin);

              if (pass_through_parent == cousin_parent &&
                  gimp_item_get_index (GIMP_ITEM (iter2->data)) < gimp_item_get_index (GIMP_ITEM (cousin)))
                {
                  /* The "cousin" layer is in the same group layer as a
                   * pass-through group and below it. We don't merge it
                   * because it will be rendered already through the
                   * merged pass-through by definition.
                   */

                  ignore = TRUE;
                  break;
                }

              cousin = cousin_parent;
            }
          while (cousin != NULL);

          if (ignore)
            break;
        }

      if (! ignore)
        {
          trimmed_list = g_slist_append (trimmed_list, layer);

          if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)) &&
              gimp_layer_get_mode (layer) == GIMP_LAYER_MODE_PASS_THROUGH)
            pass_through = g_slist_append (pass_through, layer);
        }
    }

  g_slist_free (pass_through);

  return trimmed_list;
}

static GimpLayer *
gimp_image_merge_layers (GimpImage     *image,
                         GimpContainer *container,
                         GSList        *merge_list,
                         GimpContext   *context,
                         GimpMergeType  merge_type,
                         const gchar   *undo_desc,
                         GimpProgress  *progress)
{
  GimpLayer        *parent;
  gint              x1, y1;
  gint              x2, y2;
  GSList           *layers;
  GimpLayer        *layer;
  GimpLayer        *top_layer;
  GimpLayer        *bottom_layer;
  GimpLayer        *merge_layer;
  gint              position;
  gint              n_bottom_removed = 0;
  GeglNode         *node;
  GeglNode         *source_node;
  GeglNode         *flatten_node;
  GeglNode         *offset_node;
  GeglNode         *last_node;
  GeglNode         *last_node_source = NULL;
  GimpParasiteList *parasites;
  GSList           *trimmed_list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);

  trimmed_list = gimp_image_trim_merge_list (image, merge_list);

  top_layer = trimmed_list->data;
  parent    = gimp_layer_get_parent (top_layer);

  /*  Make sure the image's graph is constructed, so that top-level layers have
   *  a parent node.
   */

  (void) gimp_projectable_get_graph (GIMP_PROJECTABLE (image));

  /*  Make sure the parent's graph is constructed, so that the top layer has a
   *  parent node, even if it is the child of a group layer (in particular, of
   *  an invisible group layer, whose graph may not have been constructed as a
   *  result of the above call.  see issue #2095.)
   */

  if (parent)
    (void) gimp_filter_get_node (GIMP_FILTER (parent));

  /*  Build our graph inside the top-layer's parent node  */
  source_node = gimp_filter_get_node (GIMP_FILTER (top_layer));
  node = gegl_node_get_parent (source_node);

  g_return_val_if_fail (node, NULL);

  /*  Get the layer extents  */
  x1 = y1 = 0;
  x2 = y2 = 0;
  for (layers = trimmed_list; layers; layers = g_slist_next (layers))
    {
      gint off_x, off_y;
      gint width, height;

      layer = layers->data;

      /* Merge down filter effects so they can be restored on undo */
      gimp_drawable_merge_filters (GIMP_DRAWABLE (layer));
      gimp_drawable_clear_filters (GIMP_DRAWABLE (layer));

      if (gimp_layer_get_mode (layer) == GIMP_LAYER_MODE_PASS_THROUGH)
        {
          GeglNode      *mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer));
          GeglRectangle  rect;

          rect   = gegl_node_get_bounding_box (mode_node);
          off_x  = rect.x;
          off_y  = rect.y;
          width  = rect.width;
          height = rect.height;
        }
      else
        {
          gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
          width  = gimp_item_get_width (GIMP_ITEM (layer));
          height = gimp_item_get_height (GIMP_ITEM (layer));
        }

      switch (merge_type)
        {
        case GIMP_EXPAND_AS_NECESSARY:
        case GIMP_CLIP_TO_IMAGE:
          if (layers == trimmed_list)
            {
              x1 = off_x;
              y1 = off_y;
              x2 = off_x + width;
              y2 = off_y + height;
            }
          else
            {
              if (off_x < x1)
                x1 = off_x;
              if (off_y < y1)
                y1 = off_y;
              if ((off_x + width) > x2)
                x2 = (off_x + width);
              if ((off_y + height) > y2)
                y2 = (off_y + height);
            }

          if (merge_type == GIMP_CLIP_TO_IMAGE)
            {
              x1 = CLAMP (x1, 0, gimp_image_get_width  (image));
              y1 = CLAMP (y1, 0, gimp_image_get_height (image));
              x2 = CLAMP (x2, 0, gimp_image_get_width  (image));
              y2 = CLAMP (y2, 0, gimp_image_get_height (image));
            }
          break;

        case GIMP_CLIP_TO_BOTTOM_LAYER:
          if (layers->next == NULL)
            {
              x1 = off_x;
              y1 = off_y;
              x2 = off_x + width;
              y2 = off_y + height;
            }
          break;

        case GIMP_FLATTEN_IMAGE:
          if (layers->next == NULL)
            {
              x1 = 0;
              y1 = 0;
              x2 = gimp_image_get_width  (image);
              y2 = gimp_image_get_height (image);
            }
          break;
        }
    }

  if ((x2 - x1) == 0 || (y2 - y1) == 0)
    {
      g_slist_free (trimmed_list);
      return NULL;
    }

  bottom_layer = layer;

  flatten_node = NULL;

  if (merge_type == GIMP_FLATTEN_IMAGE ||
      (gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)) &&
       ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))))
    {
      merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1),
                                    gimp_image_get_layer_format (image, FALSE),
                                    gimp_object_get_name (bottom_layer),
                                    GIMP_OPACITY_OPAQUE,
                                    gimp_image_get_default_new_layer_mode (image));

      if (! merge_layer)
        {
          g_warning ("%s: could not allocate merge layer", G_STRFUNC);

          g_slist_free (trimmed_list);
          return NULL;
        }

      /*  get the background for compositing  */
      flatten_node = gimp_gegl_create_flatten_node
        (gimp_context_get_background (context),
         gimp_layer_get_real_composite_space (bottom_layer));
    }
  else
    {
      /*  The final merged layer inherits the name of the bottom most layer
       *  and the resulting layer has an alpha channel whether or not the
       *  original did. Opacity is set to 100% and the MODE is set to normal.
       */


      merge_layer =
        gimp_layer_new (image, (x2 - x1), (y2 - y1),
                        gimp_drawable_get_format_with_alpha (GIMP_DRAWABLE (bottom_layer)),
                        gimp_object_get_name (bottom_layer),
                        GIMP_OPACITY_OPAQUE,
                        gimp_image_get_default_new_layer_mode (image));

      if (! merge_layer)
        {
          g_warning ("%s: could not allocate merge layer", G_STRFUNC);

          g_slist_free (trimmed_list);
          return NULL;
        }
    }

  if (merge_type == GIMP_FLATTEN_IMAGE)
    {
      position = 0;
    }
  else
    {
      /*  Find the index in the layer list of the bottom layer--we need this
       *  in order to add the final, merged layer to the layer list correctly
       */

      position =
        gimp_container_get_n_children (container) -
        gimp_container_get_child_index (container, GIMP_OBJECT (bottom_layer));
    }

  gimp_item_set_offset (GIMP_ITEM (merge_layer), x1, y1);

  offset_node = gegl_node_new_child (node,
                                     "operation", "gegl:translate",
                                     "x",         (gdouble) -x1,
                                     "y",         (gdouble) -y1,
                                     NULL);

  if (flatten_node)
    {
      gegl_node_add_child (node, flatten_node);
      g_object_unref (flatten_node);

      gegl_node_link_many (source_node, flatten_node, offset_node, NULL);
    }
  else
    {
      gegl_node_link_many (source_node, offset_node, NULL);
    }

  /*  Disconnect the bottom-layer node's input, unless it's a
   *  pass-through group.
   */

  if (gimp_layer_get_mode (bottom_layer) != GIMP_LAYER_MODE_PASS_THROUGH)
    {
      last_node        = gimp_filter_get_node (GIMP_FILTER (bottom_layer));
      last_node_source = gegl_node_get_producer (last_node, "input", NULL);

      gegl_node_disconnect (last_node, "input");
    }

  /*  Render the graph into the merge layer  */
  gimp_gegl_apply_operation (NULL, progress, undo_desc, offset_node,
                             gimp_drawable_get_buffer (
                               GIMP_DRAWABLE (merge_layer)),
                             NULL, FALSE);

  /*  Reconnect the bottom-layer node's input  */
  if (last_node_source)
    gegl_node_link (last_node_source, last_node);

  /*  Clean up the graph  */
  gegl_node_remove_child (node, offset_node);

  if (flatten_node)
    gegl_node_remove_child (node, flatten_node);

  /* Copy the tattoo and parasites of the bottom layer to the new layer */
  gimp_item_set_tattoo (GIMP_ITEM (merge_layer),
                        gimp_item_get_tattoo (GIMP_ITEM (bottom_layer)));

  parasites = gimp_item_get_parasites (GIMP_ITEM (bottom_layer));
  parasites = gimp_parasite_list_copy (parasites);
  gimp_item_set_parasites (GIMP_ITEM (merge_layer), parasites);
  g_object_unref (parasites);

  for (layers = trimmed_list; layers; layers = g_slist_next (layers))
    {
      /* Remove the sisters below merged pass-through group layers. */
      if (gimp_viewable_get_children (GIMP_VIEWABLE (layers->data)) &&
          gimp_layer_get_mode (layers->data) == GIMP_LAYER_MODE_PASS_THROUGH)
        {
          GimpLayer     *parent;
          GimpContainer *stack;
          GList         *iter;
          gboolean       remove    = FALSE;
          GList         *to_remove = NULL;

          parent = gimp_layer_get_parent (layers->data);
          if (parent)
            stack = gimp_viewable_get_children (GIMP_VIEWABLE (parent));
          else
            stack = gimp_image_get_layers (image);

          for (iter = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (stack)); iter; iter = iter->next)
            {
              if (iter->data == layers->data)
                remove = TRUE;
              else if (remove && gimp_item_get_visible (GIMP_ITEM (iter->data)))
                to_remove = g_list_prepend (to_remove, iter->data);
            }

          if (layers->data == bottom_layer)
            n_bottom_removed = g_list_length (to_remove);

          for (iter = to_remove; iter; iter = iter->next)
            gimp_image_remove_layer (image, iter->data, TRUE, NULL);

          g_list_free (to_remove);
        }

      /*  Remove the merged layers from the image  */
      gimp_image_remove_layer (image, layers->data, TRUE, NULL);
    }

  gimp_item_set_visible (GIMP_ITEM (merge_layer), TRUE, FALSE);

  /*  if the type is flatten, remove all the remaining layers  */
  if (merge_type == GIMP_FLATTEN_IMAGE)
    {
      GList *list = gimp_image_get_layer_iter (image);

      while (list)
        {
          layer = list->data;

          list = g_list_next (list);
          gimp_image_remove_layer (image, layer, TRUE, NULL);
        }

      gimp_image_add_layer (image, merge_layer, parent,
                            position, TRUE);
    }
  else
    {
      /*  Add the layer to the image  */
      gimp_image_add_layer (image, merge_layer, parent,
                            gimp_container_get_n_children (container) +
                            n_bottom_removed - position + 1,
                            TRUE);
    }

  gimp_drawable_update (GIMP_DRAWABLE (merge_layer), 0, 0, -1, -1);
  g_slist_free (trimmed_list);

  return merge_layer;
}

Messung
CHZG
82878985

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417337