Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/app/core/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 39 kB image not shown  

Quelle  gimplinklayer.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * GimpLinkLayer
 * Copyright (C) 2019 Jehan
 *
 * 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 <string.h>

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

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

#include "core-types.h"

#include "gegl/gimp-gegl-loops.h"
#include "gegl/gimp-gegl-utils.h"

#include "gimp.h"
#include "gimpcontext.h"
#include "gimpimage.h"
#include "gimpimage-color-profile.h"
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
#include "gimplink.h"
#include "gimplinklayer.h"
#include "gimpobjectqueue.h"
#include "gimpprogress.h"
#include "gimprasterizable.h"
#include "gimp-transform-utils.h"

#include "gimp-intl.h"

#define EPSILON 1e-10


enum
{
  LINK_LAYER_XCF_NONE              = 0,
  LINK_LAYER_XCF_DONT_AUTO_RENAME  = 1 << 0,
  LINK_LAYER_XCF_MODIFIED          = 1 << 1
};

enum
{
  PROP_0,
  PROP_LINK,
  PROP_SCALED_ONLY,
  N_PROPS
};

struct _GimpLinkLayerPrivate
{
  GimpLink              *link;
  gboolean               scaled_only;

  GimpMatrix3            matrix;
  gint                   offset_x;
  gint                   offset_y;
  GimpInterpolationType  interpolation;

  /* A transient value only useful to know when to drop monitoring after
   * a buffer update.
   */

  gboolean               keep_monitoring;
};

static void       gimp_link_layer_rasterizable_iface_init
                                                 (GimpRasterizableInterface *iface);

static void       gimp_link_layer_finalize       (GObject           *object);
static void       gimp_link_layer_get_property   (GObject           *object,
                                                  guint              property_id,
                                                  GValue            *value,
                                                  GParamSpec        *pspec);
static void       gimp_link_layer_set_property   (GObject           *object,
                                                  guint              property_id,
                                                  const GValue      *value,
                                                  GParamSpec        *pspec);

static gint64     gimp_link_layer_get_memsize    (GimpObject        *object,
                                                  gint64            *gui_size);

static GimpItem * gimp_link_layer_duplicate      (GimpItem          *item,
                                                  GType              new_type);

static void       gimp_link_layer_translate      (GimpItem              *item,
                                                  gdouble                offset_x,
                                                  gdouble                offset_y,
                                                  gboolean               push_undo);
static void       gimp_link_layer_flip           (GimpItem              *item,
                                                  GimpContext           *context,
                                                  GimpOrientationType    flip_type,
                                                  gdouble                axis,
                                                  gboolean               clip_result);
static void       gimp_link_layer_scale          (GimpItem              *item,
                                                  gint                   new_width,
                                                  gint                   new_height,
                                                  gint                   new_offset_x,
                                                  gint                   new_offset_y,
                                                  GimpInterpolationType  interpolation_type,
                                                  GimpProgress          *progress);
static void       gimp_link_layer_transform      (GimpItem               *item,
                                                  GimpContext            *context,
                                                  const GimpMatrix3      *matrix,
                                                  GimpTransformDirection  direction,
                                                  GimpInterpolationType   interpolation_type,
                                                  GimpTransformResize     clip_result,
                                                  GimpProgress           *progress,
                                                  gboolean                push_undo);

static void       gimp_link_layer_set_buffer     (GimpDrawable      *drawable,
                                                  gboolean           push_undo,
                                                  const gchar       *undo_desc,
                                                  GeglBuffer        *buffer,
                                                  const GeglRectangle *bounds);
static void       gimp_link_layer_push_undo      (GimpDrawable      *drawable,
                                                  const gchar       *undo_desc,
                                                  GeglBuffer        *buffer,
                                                  gint               x,
                                                  gint               y,
                                                  gint               width,
                                                  gint               height);

static void       gimp_link_layer_set_rasterized (GimpRasterizable  *rasterizable,
                                                  gboolean           rasterized);

static void       gimp_link_layer_convert_type   (GimpLayer         *layer,
                                                  GimpImage         *dest_image,
                                                  const Babl        *new_format,
                                                  GimpColorProfile  *src_profile,
                                                  GimpColorProfile  *dest_profile,
                                                  GeglDitherMethod   layer_dither_type,
                                                  GeglDitherMethod   mask_dither_type,
                                                  gboolean           push_undo,
                                                  GimpProgress      *progress);

static void       gimp_link_layer_render_full    (GimpLinkLayer     *layer);
static gboolean   gimp_link_layer_render_link    (GimpLinkLayer     *layer);

static gboolean
               gimp_link_layer_is_scaling_matrix (GimpLinkLayer     *layer,
                                                  const GimpMatrix3 *matrix,
                                                  gint              *new_width,
                                                  gint              *new_height,
                                                  gint              *new_offset_x,
                                                  gint              *new_offset_y);


G_DEFINE_TYPE_WITH_CODE (GimpLinkLayer, gimp_link_layer, GIMP_TYPE_LAYER,
                         G_ADD_PRIVATE (GimpLinkLayer)
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_RASTERIZABLE,
                                                gimp_link_layer_rasterizable_iface_init))

#define parent_class gimp_link_layer_parent_class

static GParamSpec *link_layer_props[N_PROPS] = { NULL, };


static void
gimp_link_layer_class_init (GimpLinkLayerClass *klass)
{
  GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
  GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
  GimpItemClass     *item_class        = GIMP_ITEM_CLASS (klass);
  GimpDrawableClass *drawable_class    = GIMP_DRAWABLE_CLASS (klass);
  GimpLayerClass    *layer_class       = GIMP_LAYER_CLASS (klass);

  object_class->finalize            = gimp_link_layer_finalize;
  object_class->get_property        = gimp_link_layer_get_property;
  object_class->set_property        = gimp_link_layer_set_property;

  gimp_object_class->get_memsize    = gimp_link_layer_get_memsize;

  /* TODO: make a custom icon "gimp-link-layer". */
  viewable_class->default_icon_name = "emblem-symbolic-link";
  viewable_class->default_name      = _("Link Layer");

  item_class->duplicate             = gimp_link_layer_duplicate;
  item_class->rename                = gimp_rasterizable_rename;
  item_class->translate             = gimp_link_layer_translate;
  item_class->flip                  = gimp_link_layer_flip;
  item_class->scale                 = gimp_link_layer_scale;
  item_class->transform             = gimp_link_layer_transform;

  item_class->rename_desc           = _("Rename Link Layer");
  item_class->translate_desc        = _("Move Link Layer");
  item_class->scale_desc            = _("Scale Link Layer");
  item_class->resize_desc           = _("Resize Link Layer");
  item_class->flip_desc             = _("Flip Link Layer");
  item_class->rotate_desc           = _("Rotate Link Layer");
  item_class->transform_desc        = _("Transform Link Layer");

  drawable_class->set_buffer        = gimp_link_layer_set_buffer;
  drawable_class->push_undo         = gimp_link_layer_push_undo;

  layer_class->convert_type         = gimp_link_layer_convert_type;

  link_layer_props[PROP_LINK]        = g_param_spec_object ("link",
                                                            NULL, NULL,
                                                            GIMP_TYPE_LINK,
                                                            GIMP_PARAM_READWRITE |
                                                            GIMP_PARAM_STATIC_STRINGS);

  link_layer_props[PROP_SCALED_ONLY] = g_param_spec_boolean ("scaled-only",
                                                             NULL, NULL,
                                                             FALSE,
                                                             GIMP_PARAM_READWRITE |
                                                             GIMP_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (object_class, N_PROPS, link_layer_props);
}

static void
gimp_link_layer_init (GimpLinkLayer *layer)
{
  layer->p         = gimp_link_layer_get_instance_private (layer);
  layer->p->link   = NULL;
  gimp_matrix3_identity (&layer->p->matrix);

  layer->p->scaled_only   = FALSE;
  layer->p->offset_x      = 0;
  layer->p->offset_y      = 0;
  layer->p->interpolation = GIMP_INTERPOLATION_NONE;

  layer->p->keep_monitoring = FALSE;
}

static void
gimp_link_layer_rasterizable_iface_init (GimpRasterizableInterface *iface)
{
  iface->set_rasterized = gimp_link_layer_set_rasterized;
}

static void
gimp_link_layer_finalize (GObject *object)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (object);

  g_clear_object (&layer->p->link);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gimp_link_layer_get_property (GObject      *object,
                              guint         property_id,
                              GValue       *value,
                              GParamSpec   *pspec)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (object);

  switch (property_id)
    {
    case PROP_LINK:
      g_value_set_object (value, layer->p->link);
      break;
    case PROP_SCALED_ONLY:
      g_value_set_boolean (value, layer->p->scaled_only);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_link_layer_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (object);

  switch (property_id)
    {
    case PROP_LINK:
      gimp_link_layer_set_link (layer, g_value_get_object (value), FALSE);
      break;
    case PROP_SCALED_ONLY:
      layer->p->scaled_only = g_value_get_boolean (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static gint64
gimp_link_layer_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpLinkLayer *layer   = GIMP_LINK_LAYER (object);
  gint64         memsize = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (layer->p->link),
                                      gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

static GimpItem *
gimp_link_layer_duplicate (GimpItem *item,
                           GType     new_type)
{
  GimpItem *new_item;

  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);

  new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);

  if (GIMP_IS_LINK_LAYER (new_item))
    {
      GimpLinkLayer *layer     = GIMP_LINK_LAYER (item);
      GimpLinkLayer *new_layer = GIMP_LINK_LAYER (new_item);
      GimpLink      *link      = NULL;
      GeglBuffer    *buffer;
      gint           width;
      gint           height;

      gimp_config_sync (G_OBJECT (layer), G_OBJECT (new_layer), 0);

      if (layer->p->link)
        {
          link = gimp_link_duplicate (layer->p->link);
          gimp_link_layer_set_link (new_layer, link, FALSE);
        }

      buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
      width  = gegl_buffer_get_width (buffer);
      height = gegl_buffer_get_height (buffer);

      if (width  != gimp_item_get_width  (GIMP_ITEM (new_layer)) ||
          height != gimp_item_get_height (GIMP_ITEM (new_layer)))
        {
          GeglBuffer    *new_buffer;
          GeglRectangle  bounds;

          gimp_item_get_offset (GIMP_ITEM (new_layer), &bounds.x, &bounds.y);
          bounds.width  = 0;
          bounds.height = 0;

          new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
                                        gimp_drawable_get_format (GIMP_DRAWABLE (new_layer)));
          GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (GIMP_DRAWABLE (new_layer),
                                                          FALSE, NULL,
                                                          new_buffer, &bounds);
          g_object_unref (new_buffer);
        }

      gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
                             gimp_drawable_get_buffer (GIMP_DRAWABLE (new_layer)), NULL);

      new_layer->p->scaled_only   = layer->p->scaled_only;
      gimp_rasterizable_set_auto_rename (GIMP_RASTERIZABLE (new_layer),
                                         gimp_rasterizable_get_auto_rename (GIMP_RASTERIZABLE (layer)));
      new_layer->p->matrix        = layer->p->matrix;
      new_layer->p->offset_x      = layer->p->offset_x;
      new_layer->p->offset_y      = layer->p->offset_y;
      new_layer->p->interpolation = layer->p->interpolation;

      new_layer->p->keep_monitoring = FALSE;

      if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
        gimp_rasterizable_set_undo_rasterized (GIMP_RASTERIZABLE (new_layer), TRUE);

      g_clear_object (&link);
    }

  return new_item;
}

static void
gimp_link_layer_translate (GimpItem *item,
                           gdouble   offset_x,
                           gdouble   offset_y,
                           gboolean  push_undo)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (item);

  if (! gimp_matrix3_is_identity (&layer->p->matrix))
    gimp_matrix3_translate (&layer->p->matrix, offset_x, offset_y);

  GIMP_ITEM_CLASS (parent_class)->translate (item, offset_x, offset_y, push_undo);
}

static void
gimp_link_layer_flip (GimpItem            *item,
                      GimpContext         *context,
                      GimpOrientationType  flip_type,
                      gdouble              axis,
                      gboolean             clip_result)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (item);

  if (gimp_link_is_monitored (layer->p->link))
    {
      GimpMatrix3 matrix = layer->p->matrix;

      gimp_transform_matrix_flip (&matrix, flip_type, axis);
      gimp_link_layer_set_transform (layer, &matrix, layer->p->interpolation, TRUE);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->flip (item, context, flip_type, axis, clip_result);
    }
}

static void
gimp_link_layer_scale (GimpItem              *item,
                       gint                   new_width,
                       gint                   new_height,
                       gint                   new_offset_x,
                       gint                   new_offset_y,
                       GimpInterpolationType  interpolation_type,
                       GimpProgress          *progress)
{
  GimpLinkLayer   *link_layer = GIMP_LINK_LAYER (item);
  GimpLayer       *layer      = GIMP_LAYER (item);
  GimpObjectQueue *queue      = NULL;

  if (progress && layer->mask)
    {
      GimpLayerMask *mask;

      queue    = gimp_object_queue_new (progress);
      progress = GIMP_PROGRESS (queue);

      /* temporarily set layer->mask to NULL, so that its size won't be counted
       * when pushing the layer to the queue.
       */

      mask        = layer->mask;
      layer->mask = NULL;

      gimp_object_queue_push (queue, layer);
      gimp_object_queue_push (queue, mask);

      layer->mask = mask;
    }

  if (queue)
    gimp_object_queue_pop (queue);

  if (gimp_link_is_vector (link_layer->p->link)    &&
      gimp_link_is_monitored (link_layer->p->link) &&
      gimp_matrix3_is_identity (&link_layer->p->matrix))
    {
      /* Non-modified vector images are always recomputed from the
       * source file and therefore are always sharp.
       */

      gimp_link_set_size (link_layer->p->link, new_width, new_height, FALSE);
      gimp_item_set_offset (item, new_offset_x, new_offset_y);
      gimp_link_layer_render_link (link_layer);
    }
  else if (! gimp_matrix3_is_identity (&link_layer->p->matrix))
    {
      GimpMatrix3 matrix = link_layer->p->matrix;
      gdouble     x_scale_factor;
      gdouble     y_scale_factor;
      gint        offset_x;
      gint        offset_y;

      x_scale_factor = (gdouble) new_width / gimp_item_get_width (item);
      y_scale_factor = (gdouble) new_height / gimp_item_get_height (item);

      gimp_matrix3_scale (&matrix, x_scale_factor, y_scale_factor);

      gimp_link_layer_set_transform (link_layer, &matrix, interpolation_type, TRUE);

      /* Unfortunately we can't know the proper translation offset to
       * set before we replay the full matrix (the offsets may have been
       * changed in previous transformations).
       * To avoid re-loading the source image twice though, I don't call
       * gimp_link_layer_set_transform() again but directly edit the
       * matrix and set the offset.
       */

      gimp_item_get_offset (item, &offset_x, &offset_y);
      gimp_matrix3_translate (&link_layer->p->matrix,
                              new_offset_x - offset_x,
                              new_offset_y - offset_y);
      gimp_item_set_offset (item, new_offset_x, new_offset_y);
      gimp_drawable_update_all (GIMP_DRAWABLE (layer));
    }
  else if (gimp_link_is_monitored (link_layer->p->link))
    {
      GimpMatrix3 matrix = link_layer->p->matrix;
      gdouble     x_scale_factor;
      gdouble     y_scale_factor;
      gint        offset_x;
      gint        offset_y;

      x_scale_factor = (gdouble) new_width / gimp_item_get_width (item);
      y_scale_factor = (gdouble) new_height / gimp_item_get_height (item);

      gimp_matrix3_scale (&matrix, x_scale_factor, y_scale_factor);
      gimp_item_get_offset (item, &offset_x, &offset_y);
      gimp_matrix3_translate (&matrix,
                              new_offset_x - offset_x,
                              new_offset_y - offset_y);
      gimp_link_layer_set_transform (link_layer, &matrix, interpolation_type, TRUE);
    }
  else
    {
      GIMP_ITEM_CLASS (parent_class)->scale (GIMP_ITEM (layer),
                                             new_width, new_height,
                                             new_offset_x, new_offset_y,
                                             interpolation_type, progress);
    }

  if (layer->mask)
    {
      if (queue)
        gimp_object_queue_pop (queue);

      gimp_item_scale (GIMP_ITEM (layer->mask),
                       new_width, new_height,
                       new_offset_x, new_offset_y,
                       interpolation_type, progress);
    }

  g_clear_object (&queue);
}

static void
gimp_link_layer_transform (GimpItem               *item,
                           GimpContext            *context,
                           const GimpMatrix3      *matrix,
                           GimpTransformDirection  direction,
                           GimpInterpolationType   interpolation_type,
                           GimpTransformResize     clip_result,
                           GimpProgress           *progress,
                           gboolean                push_undo)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (item);
  gboolean       keep_monitoring;

  if (gimp_matrix3_is_identity (matrix))
    return;

  if (push_undo)
    gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
                                     _("Transform Link Layer"), layer);

  if (gimp_link_is_vector (layer->p->link)    &&
      gimp_link_is_monitored (layer->p->link) &&
      gimp_matrix3_is_identity (&layer->p->matrix))
    {
      gint new_width;
      gint new_height;
      gint new_offset_x;
      gint new_offset_y;

      if (gimp_link_layer_is_scaling_matrix (layer, matrix,
                                             &new_width, &new_height,
                                             &new_offset_x, &new_offset_y))
        {
          /* Scaling when no other transformation has happened yet is
           * special-case for vector links, because we can do even
           * better by reloading from source.
           */

          gimp_link_layer_scale (item, new_width, new_height,
                                 new_offset_x, new_offset_y,
                                 interpolation_type, progress);
          return;
        }
    }

  /* Clipping would produce different results when applied on a single
   * step of multiple transformations. So only "adjust" transformations
   * are stored non-destructively. Any clip/crop transformation triggers
   * a destructive transform.
   *
   * Note: we could also store non-destructively a single clipped
   * transformation, but that would be harder to document and explain.
   */

  keep_monitoring = (gimp_link_is_monitored (layer->p->link) &&
                     clip_result == GIMP_TRANSFORM_RESIZE_ADJUST);

  if (keep_monitoring)
    {
      GimpMatrix3 left  = *matrix;
      GimpMatrix3 right = layer->p->matrix;

      if (direction == GIMP_TRANSFORM_BACKWARD)
        gimp_matrix3_invert (&left);

      gimp_matrix3_mult (&left, &right);
      gimp_link_layer_set_transform (layer, &right, interpolation_type, TRUE);
    }
  else
    {
      /* Reset the transformation matrix. */
      gimp_matrix3_identity (&layer->p->matrix);

      GIMP_ITEM_CLASS (parent_class)->transform (item, context,
                                                 matrix, direction,
                                                 interpolation_type,
                                                 clip_result, progress,
                                                 push_undo);
    }
}

static void
gimp_link_layer_set_buffer (GimpDrawable        *drawable,
                            gboolean             push_undo,
                            const gchar         *undo_desc,
                            GeglBuffer          *buffer,
                            const GeglRectangle *bounds)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
  GimpImage     *image = gimp_item_get_image (GIMP_ITEM (layer));

  if (push_undo)
    {
      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD, undo_desc);
      gimp_image_undo_push_link_layer (image, NULL, layer);
    }

  GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
                                                  FALSE, undo_desc,
                                                  buffer,
                                                  bounds);

  if (push_undo)
    {
      if (layer->p->link                          &&
          gimp_link_is_monitored (layer->p->link) &&
          ! layer->p->keep_monitoring)
        gimp_link_freeze (layer->p->link);

      gimp_image_undo_group_end (image);
    }
}

static void
gimp_link_layer_push_undo (GimpDrawable *drawable,
                           const gchar  *undo_desc,
                           GeglBuffer   *buffer,
                           gint          x,
                           gint          y,
                           gint          width,
                           gint          height)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
  GimpImage     *image = gimp_item_get_image (GIMP_ITEM (layer));
  gboolean       monitored;

  monitored = gimp_link_is_monitored (layer->p->link);

  if (monitored)
    {
      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);

      gimp_image_undo_push_link_layer (image, NULL, layer);
      gimp_link_freeze (layer->p->link);
    }

  GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
                                                 buffer,
                                                 x, y, width, height);

  if (monitored)
    {
      gimp_image_undo_group_end (image);
    }
}

static void
gimp_link_layer_set_rasterized (GimpRasterizable *rasterizable,
                                gboolean          rasterized)
{
  GimpLinkLayer *layer = GIMP_LINK_LAYER (rasterizable);

  if (rasterized)
    {
      gimp_link_freeze (layer->p->link);
    }
  else
    {
      gimp_link_thaw (layer->p->link);
      gimp_matrix3_identity (&layer->p->matrix);
      gimp_link_layer_render_link (layer);
    }
}

static void
gimp_link_layer_convert_type (GimpLayer         *layer,
                              GimpImage         *dest_image,
                              const Babl        *new_format,
                              GimpColorProfile  *src_profile,
                              GimpColorProfile  *dest_profile,
                              GeglDitherMethod   layer_dither_type,
                              GeglDitherMethod   mask_dither_type,
                              gboolean           push_undo,
                              GimpProgress      *progress)
{
  GimpLinkLayer *link_layer = GIMP_LINK_LAYER (layer);
  GimpImage     *image      = gimp_item_get_image (GIMP_ITEM (link_layer));

  GIMP_LAYER_CLASS (parent_class)->convert_type (layer, dest_image,
                                                 new_format,
                                                 src_profile,
                                                 dest_profile,
                                                 layer_dither_type,
                                                 mask_dither_type,
                                                 push_undo,
                                                 progress);

  if (link_layer->p->link && gimp_link_is_monitored (link_layer->p->link))
    {
      if (push_undo)
        gimp_image_undo_push_link_layer (image, NULL, link_layer);

      gimp_link_layer_render_link (link_layer);
    }
}


/*  public functions  */

/**
 * gimp_link_layer_new:
 * @image: the #GimpImage the layer should belong to
 * @text: a #GimpText object
 *
 * Creates a new link layer.
 *
 * Return value: a new #GimpLinkLayer or %NULL in case of a problem
 **/

GimpLayer *
gimp_link_layer_new (GimpImage *image,
                     GimpLink  *link)
{
  GimpLinkLayer *layer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  layer =
    GIMP_LINK_LAYER (gimp_drawable_new (GIMP_TYPE_LINK_LAYER,
                                        image, NULL,
                                        0, 0, 1, 1,
                                        gimp_image_get_layer_format (image,
                                                                     TRUE)));
  gimp_layer_set_mode (GIMP_LAYER (layer),
                       gimp_image_get_default_new_layer_mode (image),
                       FALSE);

  if (! gimp_link_layer_set_link (layer, link, FALSE))
    g_clear_object (&layer);

  return GIMP_LAYER (layer);
}

GimpLink *
gimp_link_layer_get_link (GimpLinkLayer *layer)
{
  g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), NULL);

  return layer->p->link;
}

gboolean
gimp_link_layer_set_link (GimpLinkLayer *layer,
                          GimpLink      *link,
                          gboolean       push_undo)
{
  return gimp_link_layer_set_link_with_matrix (layer, link, NULL,
                                               GIMP_INTERPOLATION_NONE,
                                               0, 0, push_undo);
}

gboolean
gimp_link_layer_set_link_with_matrix (GimpLinkLayer         *layer,
                                      GimpLink              *link,
                                      GimpMatrix3           *matrix,
                                      GimpInterpolationType  interpolation_type,
                                      gint                   offset_x,
                                      gint                   offset_y,
                                      gboolean               push_undo)
{
  gboolean rendered = FALSE;

  g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
  g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);

  if (gimp_item_is_attached (GIMP_ITEM (layer)) && push_undo)
    gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
                                     _("Set layer link"), layer);

  if (layer->p->link)
    {
      g_signal_handlers_disconnect_by_func (layer->p->link,
                                            G_CALLBACK (gimp_link_layer_render_full),
                                            layer);

    }

  g_set_object (&layer->p->link, link);

  if (link)
    {
      g_signal_connect_object (link, "changed",
                               G_CALLBACK (gimp_link_layer_render_full),
                               layer, G_CONNECT_SWAPPED);

      if (gimp_link_is_monitored (link))
        {
          if (matrix == NULL)
            {
              gimp_matrix3_identity (&layer->p->matrix);
              rendered = gimp_link_layer_render_link (layer);
            }
          else
            {
              if (! gimp_matrix3_is_identity (matrix))
                {
                  layer->p->offset_x = offset_x;
                  layer->p->offset_y = offset_y;
                }
              rendered = gimp_link_layer_set_transform (layer, matrix, interpolation_type, push_undo);
            }
        }
    }

  g_object_notify (G_OBJECT (layer), "link");
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));

  return rendered;
}

gboolean
gimp_link_layer_is_monitored (GimpLinkLayer *layer)
{
  g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);

  return (GIMP_LINK_LAYER (layer)->p->link &&
          gimp_link_is_monitored (GIMP_LINK_LAYER (layer)->p->link));
}

gboolean
gimp_link_layer_get_transform (GimpLinkLayer         *layer,
                               GimpMatrix3           *matrix,
                               gint                  *offset_x,
                               gint                  *offset_y,
                               GimpInterpolationType *interpolation)
{
  g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);

  if (matrix)
    *matrix = layer->p->matrix;
  if (offset_x)
    *offset_x = layer->p->offset_x;
  if (offset_y)
    *offset_y = layer->p->offset_y;
  if (interpolation)
    *interpolation = layer->p->interpolation;

  return ! gimp_matrix3_is_identity (&layer->p->matrix);
}

gboolean
gimp_link_layer_set_transform (GimpLinkLayer         *layer,
                               GimpMatrix3           *matrix,
                               GimpInterpolationType  interpolation_type,
                               gboolean               push_undo)
{
  Gimp     *gimp;
  gboolean  rendered;

  g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
  g_return_val_if_fail (gimp_link_is_monitored (layer->p->link), FALSE);

  gimp = (gimp_item_get_image (GIMP_ITEM (layer)))->gimp;

  if (gimp_matrix3_is_identity (&layer->p->matrix))
    /* First transformation: store the initial offset. */
    gimp_item_get_offset (GIMP_ITEM (layer), &layer->p->offset_x, &layer->p->offset_y);

  gimp_item_set_offset (GIMP_ITEM (layer), layer->p->offset_x, layer->p->offset_y);

  rendered = gimp_link_layer_render_link (layer);
  if (! gimp_matrix3_is_identity (matrix))
    {
      layer->p->keep_monitoring = TRUE;
      GIMP_ITEM_CLASS (parent_class)->transform (GIMP_ITEM (layer),
                                                 gimp_get_user_context (gimp),
                                                 matrix,
                                                 GIMP_TRANSFORM_FORWARD,
                                                 interpolation_type,
                                                 GIMP_TRANSFORM_RESIZE_ADJUST,
                                                 NULL, push_undo);
      layer->p->keep_monitoring = FALSE;
    }

  layer->p->matrix = *matrix;

  /* Interpolations are used to obtain reasonable quality. Considering
   * that doing a single transform will always be better than several
   * transforms in a row, it's OK to just drop the interpolation
   * algorithm of previous transforms. We just store the last one.
   */

  layer->p->interpolation = interpolation_type;

  return rendered;
}

gboolean
gimp_item_is_link_layer (GimpItem *item)
{
  g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);

  return (GIMP_IS_LINK_LAYER (item) &&
          gimp_link_layer_is_monitored (GIMP_LINK_LAYER (item)));
}

void
gimp_link_layer_set_xcf_flags (GimpLinkLayer *layer,
                               guint32        flags)
{
  g_return_if_fail (GIMP_IS_LINK_LAYER (layer));

  gimp_rasterizable_set_auto_rename (GIMP_RASTERIZABLE (layer),
                                     (flags & LINK_LAYER_XCF_DONT_AUTO_RENAME) == 0);

  if ((flags & LINK_LAYER_XCF_MODIFIED) != 0)
    {
      gimp_link_freeze (layer->p->link);
      gimp_rasterizable_set_undo_rasterized (GIMP_RASTERIZABLE (layer), TRUE);
    }
}

guint32
gimp_link_layer_get_xcf_flags (GimpLinkLayer *link_layer)
{
  guint flags = 0;

  g_return_val_if_fail (GIMP_IS_LINK_LAYER (link_layer), 0);

  if (! gimp_rasterizable_get_auto_rename (GIMP_RASTERIZABLE (link_layer)))
    flags |= LINK_LAYER_XCF_DONT_AUTO_RENAME;

  if (! gimp_link_is_monitored (link_layer->p->link))
    flags |= LINK_LAYER_XCF_MODIFIED;

  return flags;
}


/*  private functions  */

static void
gimp_link_layer_render_full (GimpLinkLayer *layer)
{
  gimp_link_layer_render_link (layer);
  if (! gimp_matrix3_is_identity (&layer->p->matrix))
    {
      Gimp *gimp = (gimp_item_get_image (GIMP_ITEM (layer)))->gimp;

      gimp_item_set_offset (GIMP_ITEM (layer), layer->p->offset_x, layer->p->offset_y);

      layer->p->keep_monitoring = TRUE;
      GIMP_ITEM_CLASS (parent_class)->transform (GIMP_ITEM (layer),
                                                 gimp_get_user_context (gimp),
                                                 &layer->p->matrix,
                                                 GIMP_TRANSFORM_FORWARD,
                                                 layer->p->interpolation,
                                                 GIMP_TRANSFORM_RESIZE_ADJUST,
                                                 NULL, FALSE);
      layer->p->keep_monitoring = FALSE;
    }
}

static gboolean
gimp_link_layer_render_link (GimpLinkLayer *layer)
{
  GimpDrawable   *drawable;
  GimpItem       *item;
  GimpImage      *image;
  GeglBuffer     *buffer;
  gdouble         xres;
  gdouble         yres;
  gint            width;
  gint            height;

  if (! layer->p->link)
    return FALSE;

  drawable = GIMP_DRAWABLE (layer);
  item     = GIMP_ITEM (layer);
  image    = gimp_item_get_image (item);

  gimp_image_get_resolution (image, &xres, &yres);
  /* TODO: I could imagine a GimpBusyBox (to be made as GimpProgress) in
   * the later list showing layer update.
   */

  buffer = gimp_link_get_buffer (layer->p->link);

  if (! buffer)
    return FALSE;

  width  = gegl_buffer_get_width (buffer);
  height = gegl_buffer_get_height (buffer);

  g_object_freeze_notify (G_OBJECT (drawable));

  if ((width  != gimp_item_get_width  (item) ||
       height != gimp_item_get_height (item)))
    {
      GeglBuffer    *new_buffer;
      GeglRectangle  bounds;

      gimp_item_get_offset (GIMP_ITEM (drawable),
                            &bounds.x, &bounds.y);
      bounds.width  = 0;
      bounds.height = 0;

      new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
                                    gimp_drawable_get_format (drawable));
      GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
                                                      FALSE, NULL,
                                                      new_buffer, &bounds);
      g_object_unref (new_buffer);

      if (gimp_layer_get_mask (GIMP_LAYER (layer)))
        {
          GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));

          static GimpContext *unused_eek = NULL;

          if (! unused_eek)
            unused_eek = gimp_context_new (image->gimp, "eek", NULL);

          gimp_item_resize (GIMP_ITEM (mask),
                            unused_eek, GIMP_FILL_TRANSPARENT,
                            width, height, 0, 0);
        }
    }

  gimp_rasterizable_auto_rename (GIMP_RASTERIZABLE (layer),
                                 GIMP_OBJECT (layer->p->link),
                                 NULL);

  gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
                         gimp_drawable_get_buffer (drawable), NULL);

  g_object_thaw_notify (G_OBJECT (drawable));

  gimp_drawable_update (drawable, 0, 0, width, height);
  gimp_image_flush (image);

  return (width > 0 && height > 0);
}

static gboolean
gimp_link_layer_is_scaling_matrix (GimpLinkLayer     *layer,
                                   const GimpMatrix3 *matrix,
                                   gint              *new_width,
                                   gint              *new_height,
                                   gint              *new_offset_x,
                                   gint              *new_offset_y)
{
  gboolean is_scaling;

  /* Scaling 3x3 matrix on a 2D plane (with optional translation). */
  is_scaling = (matrix->coeff[0][0] > 0.0 && matrix->coeff[0][1] == 0.0 &&
                matrix->coeff[1][0] < EPSILON && matrix->coeff[1][1] > 0.0 &&
                matrix->coeff[2][0] < EPSILON && matrix->coeff[2][1] < EPSILON && matrix->coeff[2][2] == 1.0);

  if (is_scaling)
    {
      gint width;
      gint height;
      gint offset_x;
      gint offset_y;

      width  = gimp_item_get_width (GIMP_ITEM (layer));
      height = gimp_item_get_height (GIMP_ITEM (layer));
      gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);

      *new_width  = (gint) (width * matrix->coeff[0][0]);
      *new_height = (gint) (height * matrix->coeff[1][1]);

      *new_offset_x = (gint) (offset_x * matrix->coeff[0][0] + matrix->coeff[0][2]);
      *new_offset_y = (gint) (offset_y * matrix->coeff[1][1] + matrix->coeff[1][2]);
    }

  return is_scaling;
}

Messung
CHZG
811009592

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© 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.