/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-2003 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/>.
 */

/* NOTE: This file is auto-generated by pdbgen.pl. */

#include "config.h"

#include <gegl.h>

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

#include "libgimpmath/gimpmath.h"

#include "libgimpbase/gimpbase.h"

#include "pdb-types.h"

#include "core/gimp-transform-utils.h"
#include "core/gimpchannel.h"
#include "core/gimpdrawable-transform.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpitem-linked.h"
#include "core/gimpitem.h"
#include "core/gimpparamspecs.h"
#include "core/gimpprogress.h"

#include "gimppdb.h"
#include "gimppdb-utils.h"
#include "gimppdbcontext.h"
#include "gimpprocedure.h"
#include "internal-procs.h"

#include "gimp-intl.h"


static GimpValueArray *
item_transform_translate_invoker (GimpProcedure         *procedure,
                                  Gimp                  *gimp,
                                  GimpContext           *context,
                                  GimpProgress          *progress,
                                  const GimpValueArray  *args,
                                  GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble off_x;
  gdouble off_y;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  off_x = g_value_get_double (gimp_value_array_index (args, 1));
  off_y = g_value_get_double (gimp_value_array_index (args, 2));

  if (success)
    {
      if (gimp_pdb_item_is_modifiable (item,
                                       GIMP_PDB_ITEM_POSITION, error))
        {
          if (gimp_item_get_linked (item) && gimp_item_is_attached (item))
            {
              gimp_item_linked_translate (item, off_x, off_y, TRUE);
            }
          else
            {
              gimp_item_translate (item, off_x, off_y, TRUE);
            }
        }
      else
        success = FALSE;
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_flip_simple_invoker (GimpProcedure         *procedure,
                                    Gimp                  *gimp,
                                    GimpContext           *context,
                                    GimpProgress          *progress,
                                    const GimpValueArray  *args,
                                    GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gint32 flip_type;
  gboolean auto_center;
  gdouble axis;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  flip_type = g_value_get_enum (gimp_value_array_index (args, 1));
  auto_center = g_value_get_boolean (gimp_value_array_index (args, 2));
  axis = g_value_get_double (gimp_value_array_index (args, 3));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          gimp_transform_get_flip_axis (x, y, width, height,
                                        flip_type, auto_center, &axis);

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_flip (GIMP_DRAWABLE (item), context,
                                                       flip_type, axis,
                                                       pdb_context->transform_resize);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_flip (item, context,
                                     flip_type, axis,
                                     pdb_context->transform_resize);
            }
          else
            {
              gimp_item_flip (item, context,
                              flip_type, axis,
                              pdb_context->transform_resize);
            }
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_flip_invoker (GimpProcedure         *procedure,
                             Gimp                  *gimp,
                             GimpContext           *context,
                             GimpProgress          *progress,
                             const GimpValueArray  *args,
                             GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble x0;
  gdouble y0;
  gdouble x1;
  gdouble y1;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  x0 = g_value_get_double (gimp_value_array_index (args, 1));
  y0 = g_value_get_double (gimp_value_array_index (args, 2));
  x1 = g_value_get_double (gimp_value_array_index (args, 3));
  y1 = g_value_get_double (gimp_value_array_index (args, 4));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity (&matrix);
          gimp_transform_matrix_flip_free (&matrix, x0, y0, x1, y1);

          if (progress)
            gimp_progress_start (progress, FALSE, _("Flipping"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_perspective_invoker (GimpProcedure         *procedure,
                                    Gimp                  *gimp,
                                    GimpContext           *context,
                                    GimpProgress          *progress,
                                    const GimpValueArray  *args,
                                    GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble x0;
  gdouble y0;
  gdouble x1;
  gdouble y1;
  gdouble x2;
  gdouble y2;
  gdouble x3;
  gdouble y3;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  x0 = g_value_get_double (gimp_value_array_index (args, 1));
  y0 = g_value_get_double (gimp_value_array_index (args, 2));
  x1 = g_value_get_double (gimp_value_array_index (args, 3));
  y1 = g_value_get_double (gimp_value_array_index (args, 4));
  x2 = g_value_get_double (gimp_value_array_index (args, 5));
  y2 = g_value_get_double (gimp_value_array_index (args, 6));
  x3 = g_value_get_double (gimp_value_array_index (args, 7));
  y3 = g_value_get_double (gimp_value_array_index (args, 8));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity (&matrix);
          gimp_transform_matrix_perspective (&matrix,
                                             x, y, width, height,
                                             x0, y0, x1, y1,
                                             x2, y2, x3, y3);

          if (progress)
            gimp_progress_start (progress, FALSE, _("Perspective"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_rotate_simple_invoker (GimpProcedure         *procedure,
                                      Gimp                  *gimp,
                                      GimpContext           *context,
                                      GimpProgress          *progress,
                                      const GimpValueArray  *args,
                                      GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gint32 rotate_type;
  gboolean auto_center;
  gdouble center_x;
  gdouble center_y;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  rotate_type = g_value_get_enum (gimp_value_array_index (args, 1));
  auto_center = g_value_get_boolean (gimp_value_array_index (args, 2));
  center_x = g_value_get_double (gimp_value_array_index (args, 3));
  center_y = g_value_get_double (gimp_value_array_index (args, 4));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          gimp_transform_get_rotate_center (x, y, width, height,
                                            auto_center, &center_x, &center_y);

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_rotate (GIMP_DRAWABLE (item),
                                                         context,
                                                         rotate_type,
                                                         center_x, center_y,
                                                         pdb_context->transform_resize);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_rotate (item, context,
                                       rotate_type,
                                       center_x, center_y,
                                       pdb_context->transform_resize);
            }
          else
            {
              gimp_item_rotate (item, context,
                                rotate_type,
                                center_x, center_y,
                                pdb_context->transform_resize);
            }
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_rotate_invoker (GimpProcedure         *procedure,
                               Gimp                  *gimp,
                               GimpContext           *context,
                               GimpProgress          *progress,
                               const GimpValueArray  *args,
                               GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble angle;
  gboolean auto_center;
  gdouble center_x;
  gdouble center_y;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  angle = g_value_get_double (gimp_value_array_index (args, 1));
  auto_center = g_value_get_boolean (gimp_value_array_index (args, 2));
  center_x = g_value_get_double (gimp_value_array_index (args, 3));
  center_y = g_value_get_double (gimp_value_array_index (args, 4));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity (&matrix);
          if (auto_center)
            gimp_transform_matrix_rotate_rect (&matrix,
                                               x, y, width, height, angle);
          else
            gimp_transform_matrix_rotate_center (&matrix,
                                                 center_x, center_y, angle);

          if (progress)
            gimp_progress_start (progress, FALSE, _("Rotating"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_scale_invoker (GimpProcedure         *procedure,
                              Gimp                  *gimp,
                              GimpContext           *context,
                              GimpProgress          *progress,
                              const GimpValueArray  *args,
                              GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble x0;
  gdouble y0;
  gdouble x1;
  gdouble y1;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  x0 = g_value_get_double (gimp_value_array_index (args, 1));
  y0 = g_value_get_double (gimp_value_array_index (args, 2));
  x1 = g_value_get_double (gimp_value_array_index (args, 3));
  y1 = g_value_get_double (gimp_value_array_index (args, 4));

  if (success)
    {
      gint x, y, width, height;

      success = (gimp_pdb_item_is_attached (item, NULL,
                                            GIMP_PDB_ITEM_CONTENT |
                                            GIMP_PDB_ITEM_POSITION, error) &&
                 x0 < x1 && y0 < y1);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity (&matrix);
          gimp_transform_matrix_scale (&matrix,
                                       x, y, width, height,
                                       x0, y0, x1 - x0, y1 - y0);

          if (progress)
            gimp_progress_start (progress, FALSE, _("Scaling"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_shear_invoker (GimpProcedure         *procedure,
                              Gimp                  *gimp,
                              GimpContext           *context,
                              GimpProgress          *progress,
                              const GimpValueArray  *args,
                              GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gint32 shear_type;
  gdouble magnitude;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  shear_type = g_value_get_enum (gimp_value_array_index (args, 1));
  magnitude = g_value_get_double (gimp_value_array_index (args, 2));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity (&matrix);
          gimp_transform_matrix_shear (&matrix,
                                       x, y, width, height,
                                       shear_type, magnitude);

          if (progress)
            gimp_progress_start (progress, FALSE, _("Shearing"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_2d_invoker (GimpProcedure         *procedure,
                           Gimp                  *gimp,
                           GimpContext           *context,
                           GimpProgress          *progress,
                           const GimpValueArray  *args,
                           GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble source_x;
  gdouble source_y;
  gdouble scale_x;
  gdouble scale_y;
  gdouble angle;
  gdouble dest_x;
  gdouble dest_y;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  source_x = g_value_get_double (gimp_value_array_index (args, 1));
  source_y = g_value_get_double (gimp_value_array_index (args, 2));
  scale_x = g_value_get_double (gimp_value_array_index (args, 3));
  scale_y = g_value_get_double (gimp_value_array_index (args, 4));
  angle = g_value_get_double (gimp_value_array_index (args, 5));
  dest_x = g_value_get_double (gimp_value_array_index (args, 6));
  dest_y = g_value_get_double (gimp_value_array_index (args, 7));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          gimp_matrix3_identity  (&matrix);
          gimp_matrix3_translate (&matrix, -source_x, -source_y);
          gimp_matrix3_scale     (&matrix, scale_x, scale_y);
          gimp_matrix3_rotate    (&matrix, angle);
          gimp_matrix3_translate (&matrix, dest_x, dest_y);

          if (progress)
            gimp_progress_start (progress, FALSE, _("2D Transform"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

static GimpValueArray *
item_transform_matrix_invoker (GimpProcedure         *procedure,
                               Gimp                  *gimp,
                               GimpContext           *context,
                               GimpProgress          *progress,
                               const GimpValueArray  *args,
                               GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpItem *item;
  gdouble coeff_0_0;
  gdouble coeff_0_1;
  gdouble coeff_0_2;
  gdouble coeff_1_0;
  gdouble coeff_1_1;
  gdouble coeff_1_2;
  gdouble coeff_2_0;
  gdouble coeff_2_1;
  gdouble coeff_2_2;

  item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
  coeff_0_0 = g_value_get_double (gimp_value_array_index (args, 1));
  coeff_0_1 = g_value_get_double (gimp_value_array_index (args, 2));
  coeff_0_2 = g_value_get_double (gimp_value_array_index (args, 3));
  coeff_1_0 = g_value_get_double (gimp_value_array_index (args, 4));
  coeff_1_1 = g_value_get_double (gimp_value_array_index (args, 5));
  coeff_1_2 = g_value_get_double (gimp_value_array_index (args, 6));
  coeff_2_0 = g_value_get_double (gimp_value_array_index (args, 7));
  coeff_2_1 = g_value_get_double (gimp_value_array_index (args, 8));
  coeff_2_2 = g_value_get_double (gimp_value_array_index (args, 9));

  if (success)
    {
      gint x, y, width, height;

      success = gimp_pdb_item_is_attached (item, NULL,
                                           GIMP_PDB_ITEM_CONTENT |
                                           GIMP_PDB_ITEM_POSITION, error);

      if (success &&
          gimp_item_mask_intersect (item, &x, &y, &width, &height))
        {
          GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
          GimpMatrix3     matrix;
          gint            off_x, off_y;

          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;

          /* Assemble the transformation matrix */
          matrix.coeff[0][0] = coeff_0_0;
          matrix.coeff[0][1] = coeff_0_1;
          matrix.coeff[0][2] = coeff_0_2;
          matrix.coeff[1][0] = coeff_1_0;
          matrix.coeff[1][1] = coeff_1_1;
          matrix.coeff[1][2] = coeff_1_2;
          matrix.coeff[2][0] = coeff_2_0;
          matrix.coeff[2][1] = coeff_2_1;
          matrix.coeff[2][2] = coeff_2_2;

          if (progress)
            gimp_progress_start (progress, FALSE, _("2D Transforming"));

          if (GIMP_IS_DRAWABLE (item) &&
              ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
              ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
            {
              GimpDrawable *drawable;

              drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
                                                         context, &matrix,
                                                         pdb_context->transform_direction,
                                                         pdb_context->interpolation,
                                                         pdb_context->transform_resize,
                                                         progress);

              if (drawable)
                item = GIMP_ITEM (drawable);
              else
                success = FALSE;
            }
          else if (gimp_item_get_linked (item))
            {
              gimp_item_linked_transform (item, context, &matrix,
                                          pdb_context->transform_direction,
                                          pdb_context->interpolation,
                                          pdb_context->transform_resize,
                                          progress);
            }
          else
            {
              gimp_item_transform (item, context, &matrix,
                                   pdb_context->transform_direction,
                                   pdb_context->interpolation,
                                   pdb_context->transform_resize,
                                   progress);
            }

          if (progress)
            gimp_progress_end (progress);
        }
    }

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  if (success)
    gimp_value_set_item (gimp_value_array_index (return_vals, 1), item);

  return return_vals;
}

void
register_item_transform_procs (GimpPDB *pdb)
{
  GimpProcedure *procedure;

  /*
   * gimp-item-transform-translate
   */
  procedure = gimp_procedure_new (item_transform_translate_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-translate");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-translate",
                                     "Translate the item by the specified offsets.",
                                     "This procedure translates the item by the amounts specified in the off_x and off_y arguments. These can be negative, and are considered offsets from the current position. The offsets will be rounded to the nearest pixel unless the item is a path.\n"
                                     "\n"
                                     "If the item is attached to an image and has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be translated by the specified offsets.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2018",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("off-x",
                                                    "off x",
                                                    "Offset in x direction",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("off-y",
                                                    "off y",
                                                    "Offset in y direction",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The translated item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-flip-simple
   */
  procedure = gimp_procedure_new (item_transform_flip_simple_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-flip-simple");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-flip-simple",
                                     "Flip the specified item either vertically or horizontally.",
                                     "This procedure flips the specified item.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then flipped. If auto_center is set to TRUE, the flip is around the selection's center. Otherwise, the coordinate of the axis needs to be specified. The return value is the ID of the flipped floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be flipped around its center if auto_center is set to TRUE, otherwise the coordinate of the axis needs to be specified. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be flipped around the same axis. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2004",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_enum ("flip-type",
                                                     "flip type",
                                                     "Type of flip",
                                                     GIMP_TYPE_ORIENTATION_TYPE,
                                                     GIMP_ORIENTATION_HORIZONTAL,
                                                     GIMP_PARAM_READWRITE));
  gimp_param_spec_enum_exclude_value (GIMP_PARAM_SPEC_ENUM (procedure->args[1]),
                                      GIMP_ORIENTATION_UNKNOWN);
  gimp_procedure_add_argument (procedure,
                               g_param_spec_boolean ("auto-center",
                                                     "auto center",
                                                     "Whether to automatically position the axis in the selection center",
                                                     FALSE,
                                                     GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("axis",
                                                    "axis",
                                                    "coord. of flip axis",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The flipped item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-flip
   */
  procedure = gimp_procedure_new (item_transform_flip_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-flip");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-flip",
                                     "Flip the specified item around a given line.",
                                     "This procedure flips the specified item.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then flipped. The axis to flip around is specified by specifying two points from that line. The return value is the ID of the flipped floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be flipped around the specified axis. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be flipped around the same axis. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x0",
                                                    "x0",
                                                    "horz. coord. of one end of axis",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y0",
                                                    "y0",
                                                    "vert. coord. of one end of axis",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x1",
                                                    "x1",
                                                    "horz. coord. of other end of axis",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y1",
                                                    "y1",
                                                    "vert. coord. of other end of axis",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The flipped item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-perspective
   */
  procedure = gimp_procedure_new (item_transform_perspective_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-perspective");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-perspective",
                                     "Perform a possibly non-affine transformation on the specified item.",
                                     "This procedure performs a possibly non-affine transformation on the specified item by allowing the corners of the original bounding box to be arbitrarily remapped to any values.\n"
                                     "\n"
                                     "The 4 coordinates specify the new locations of each corner of the original bounding box. By specifying these values, any affine transformation (rotation, scaling, translation) can be affected. Additionally, these values can be specified such that the resulting transformed item will appear to have been projected via a perspective transform.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then transformed as specified. The return value is the ID of the transformed floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be transformed according to the specified mapping. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be transformed the same way. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x0",
                                                    "x0",
                                                    "The new x coordinate of upper-left corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y0",
                                                    "y0",
                                                    "The new y coordinate of upper-left corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x1",
                                                    "x1",
                                                    "The new x coordinate of upper-right corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y1",
                                                    "y1",
                                                    "The new y coordinate of upper-right corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x2",
                                                    "x2",
                                                    "The new x coordinate of lower-left corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y2",
                                                    "y2",
                                                    "The new y coordinate of lower-left corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x3",
                                                    "x3",
                                                    "The new x coordinate of lower-right corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y3",
                                                    "y3",
                                                    "The new y coordinate of lower-right corner of original bounding box",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The transformed item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-rotate-simple
   */
  procedure = gimp_procedure_new (item_transform_rotate_simple_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-rotate-simple");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-rotate-simple",
                                     "Rotate the specified item about given coordinates through the specified angle.",
                                     "This function rotates the specified item.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then rotated by the specified amount. If auto_center is set to TRUE, the rotation is around the selection's center. Otherwise, the coordinate of the center point needs to be specified. The return value is the ID of the rotated floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be rotated around its center if auto_center is set to TRUE, otherwise the coordinate of the center point needs to be specified. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be rotated around the same center point. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_enum ("rotate-type",
                                                  "rotate type",
                                                  "Type of rotation",
                                                  GIMP_TYPE_ROTATION_TYPE,
                                                  GIMP_ROTATE_90,
                                                  GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_boolean ("auto-center",
                                                     "auto center",
                                                     "Whether to automatically rotate around the selection center",
                                                     FALSE,
                                                     GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("center-x",
                                                    "center x",
                                                    "The hor. coordinate of the center of rotation",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("center-y",
                                                    "center y",
                                                    "The vert. coordinate of the center of rotation",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The rotated item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-rotate
   */
  procedure = gimp_procedure_new (item_transform_rotate_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-rotate");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-rotate",
                                     "Rotate the specified item about given coordinates through the specified angle.",
                                     "This function rotates the specified item.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then rotated by the specified amount. If auto_center is set to TRUE, the rotation is around the selection's center. Otherwise, the coordinate of the center point needs to be specified. The return value is the ID of the rotated floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be rotated around its center if auto_center is set to TRUE, otherwise the coordinate of the center point needs to be specified. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be rotated around the same center point. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("angle",
                                                    "angle",
                                                    "The angle of rotation (radians)",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_boolean ("auto-center",
                                                     "auto center",
                                                     "Whether to automatically rotate around the selection center",
                                                     FALSE,
                                                     GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("center-x",
                                                    "center x",
                                                    "The hor. coordinate of the center of rotation",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("center-y",
                                                    "center y",
                                                    "The vert. coordinate of the center of rotation",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The rotated item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-scale
   */
  procedure = gimp_procedure_new (item_transform_scale_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-scale");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-scale",
                                     "Scale the specified item.",
                                     "This procedure scales the specified item.\n"
                                     "\n"
                                     "The 2 coordinates specify the new locations of the top-left and bottom-roght corners of the original bounding box.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then scaled as specified. The return value is the ID of the scaled floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be scaled according to the specified coordinates. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be scaled the same way. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x0",
                                                    "x0",
                                                    "The new x coordinate of the upper-left corner of the scaled region",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y0",
                                                    "y0",
                                                    "The new y coordinate of the upper-left corner of the scaled region",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("x1",
                                                    "x1",
                                                    "The new x coordinate of the lower-right corner of the scaled region",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("y1",
                                                    "y1",
                                                    "The new y coordinate of the lower-right corner of the scaled region",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The scaled item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-shear
   */
  procedure = gimp_procedure_new (item_transform_shear_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-shear");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-shear",
                                     "Shear the specified item about its center by the specified magnitude.",
                                     "This procedure shears the specified item.\n"
                                     "\n"
                                     "The shear type parameter indicates whether the shear will be applied horizontally or vertically. The magnitude can be either positive or negative and indicates the extent (in pixels) to shear by.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then sheared as specified. The return value is the ID of the sheared floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be sheared according to the specified parameters. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be sheared the same way. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_enum ("shear-type",
                                                     "shear type",
                                                     "Type of shear",
                                                     GIMP_TYPE_ORIENTATION_TYPE,
                                                     GIMP_ORIENTATION_HORIZONTAL,
                                                     GIMP_PARAM_READWRITE));
  gimp_param_spec_enum_exclude_value (GIMP_PARAM_SPEC_ENUM (procedure->args[1]),
                                      GIMP_ORIENTATION_UNKNOWN);
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("magnitude",
                                                    "magnitude",
                                                    "The magnitude of the shear",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The sheared item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-2d
   */
  procedure = gimp_procedure_new (item_transform_2d_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-2d");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-2d",
                                     "Transform the specified item in 2d.",
                                     "This procedure transforms the specified item.\n"
                                     "\n"
                                     "The transformation is done by scaling by the x and y scale factors about the point (source_x, source_y), then rotating around the same point, then translating that point to the new position (dest_x, dest_y).\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then transformed as specified. The return value is the ID of the transformed floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be transformed according to the specified parameters. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be transformed the same way. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("source-x",
                                                    "source x",
                                                    "X coordinate of the transformation center",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("source-y",
                                                    "source y",
                                                    "Y coordinate of the transformation center",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("scale-x",
                                                    "scale x",
                                                    "Amount to scale in x direction",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("scale-y",
                                                    "scale y",
                                                    "Amount to scale in y direction",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("angle",
                                                    "angle",
                                                    "The angle of rotation (radians)",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("dest-x",
                                                    "dest x",
                                                    "X coordinate of where the center goes",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("dest-y",
                                                    "dest y",
                                                    "Y coordinate of where the center goes",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The transformed item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);

  /*
   * gimp-item-transform-matrix
   */
  procedure = gimp_procedure_new (item_transform_matrix_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-item-transform-matrix");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-item-transform-matrix",
                                     "Transform the specified item in 2d.",
                                     "This procedure transforms the specified item.\n"
                                     "\n"
                                     "The transformation is done by assembling a 3x3 matrix from the coefficients passed.\n"
                                     "\n"
                                     "If a selection exists and the item is a drawable, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then transformed as specified. The return value is the ID of the transformed floating selection.\n"
                                     "\n"
                                     "If there is no selection or the item is not a drawable, the entire item will be transformed according to the specified matrix. Additionally, if the item has its linked flag set to TRUE, all additional items contained in the image which have the linked flag set to TRUE will also be transformed the same way. The return value will be equal to the item ID supplied as input.\n"
                                     "\n"
                                     "This procedure is affected by the following context setters: 'gimp-context-set-interpolation', 'gimp-context-set-transform-direction', 'gimp-context-set-transform-resize'.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2010",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_item_id ("item",
                                                        "item",
                                                        "The affected item",
                                                        pdb->gimp, FALSE,
                                                        GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-0-0",
                                                    "coeff 0 0",
                                                    "coefficient (0,0) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-0-1",
                                                    "coeff 0 1",
                                                    "coefficient (0,1) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-0-2",
                                                    "coeff 0 2",
                                                    "coefficient (0,2) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-1-0",
                                                    "coeff 1 0",
                                                    "coefficient (1,0) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-1-1",
                                                    "coeff 1 1",
                                                    "coefficient (1,1) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-1-2",
                                                    "coeff 1 2",
                                                    "coefficient (1,2) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-2-0",
                                                    "coeff 2 0",
                                                    "coefficient (2,0) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-2-1",
                                                    "coeff 2 1",
                                                    "coefficient (2,1) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_argument (procedure,
                               g_param_spec_double ("coeff-2-2",
                                                    "coeff 2 2",
                                                    "coefficient (2,2) of the transformation matrix",
                                                    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                    GIMP_PARAM_READWRITE));
  gimp_procedure_add_return_value (procedure,
                                   gimp_param_spec_item_id ("item",
                                                            "item",
                                                            "The transformed item",
                                                            pdb->gimp, FALSE,
                                                            GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);
}
