/* The GIMP -- an image manipulation program
 * Copyright (C) 1995-2000 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* NOTE: This file is autogenerated by pdbgen.pl. */

#include "config.h"


#include <gtk/gtk.h>

#include "libgimpbase/gimpbasetypes.h"

#include "pdb-types.h"
#include "tools/tools-types.h"
#include "procedural_db.h"

#include "base/base-types.h"
#include "base/tile-manager.h"
#include "core/core-types.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage-mask-select.h"
#include "core/gimpimage.h"
#include "tools/gimpairbrushtool.h"
#include "tools/gimpblendtool.h"
#include "tools/gimpbucketfilltool.h"
#include "tools/gimpclonetool.h"
#include "tools/gimpcolorpickertool.h"
#include "tools/gimpconvolvetool.h"
#include "tools/gimpdodgeburntool.h"
#include "tools/gimperasertool.h"
#include "tools/gimpfliptool.h"
#include "tools/gimppaintbrushtool.h"
#include "tools/gimppenciltool.h"
#include "tools/gimpperspectivetool.h"
#include "tools/gimprotatetool.h"
#include "tools/gimpscaletool.h"
#include "tools/gimpsheartool.h"
#include "tools/gimpsmudgetool.h"
#include "tools/gimptransformtool.h"
#include "undo.h"

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

static ProcRecord airbrush_proc;
static ProcRecord airbrush_default_proc;
static ProcRecord blend_proc;
static ProcRecord bucket_fill_proc;
static ProcRecord clone_proc;
static ProcRecord clone_default_proc;
static ProcRecord color_picker_proc;
static ProcRecord convolve_proc;
static ProcRecord convolve_default_proc;
static ProcRecord dodgeburn_proc;
static ProcRecord dodgeburn_default_proc;
static ProcRecord eraser_proc;
static ProcRecord eraser_default_proc;
static ProcRecord flip_proc;
static ProcRecord paintbrush_proc;
static ProcRecord paintbrush_default_proc;
static ProcRecord pencil_proc;
static ProcRecord perspective_proc;
static ProcRecord rotate_proc;
static ProcRecord scale_proc;
static ProcRecord shear_proc;
static ProcRecord smudge_proc;
static ProcRecord smudge_default_proc;
static ProcRecord transform_2d_proc;

void
register_tools_procs (Gimp *gimp)
{
  procedural_db_register (gimp, &airbrush_proc);
  procedural_db_register (gimp, &airbrush_default_proc);
  procedural_db_register (gimp, &blend_proc);
  procedural_db_register (gimp, &bucket_fill_proc);
  procedural_db_register (gimp, &clone_proc);
  procedural_db_register (gimp, &clone_default_proc);
  procedural_db_register (gimp, &color_picker_proc);
  procedural_db_register (gimp, &convolve_proc);
  procedural_db_register (gimp, &convolve_default_proc);
  procedural_db_register (gimp, &dodgeburn_proc);
  procedural_db_register (gimp, &dodgeburn_default_proc);
  procedural_db_register (gimp, &eraser_proc);
  procedural_db_register (gimp, &eraser_default_proc);
  procedural_db_register (gimp, &flip_proc);
  procedural_db_register (gimp, &paintbrush_proc);
  procedural_db_register (gimp, &paintbrush_default_proc);
  procedural_db_register (gimp, &pencil_proc);
  procedural_db_register (gimp, &perspective_proc);
  procedural_db_register (gimp, &rotate_proc);
  procedural_db_register (gimp, &scale_proc);
  procedural_db_register (gimp, &shear_proc);
  procedural_db_register (gimp, &smudge_proc);
  procedural_db_register (gimp, &smudge_default_proc);
  procedural_db_register (gimp, &transform_2d_proc);
}

static Argument *
airbrush_invoker (Gimp     *gimp,
                  Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gdouble pressure;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  pressure = args[1].value.pdb_float;
  if (pressure < 0.0 || pressure > 100.0)
    success = FALSE;

  num_strokes = args[2].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[3].value.pdb_pointer;

  if (success)
    success = airbrush_non_gui (drawable, pressure, num_strokes, strokes);

  return procedural_db_return_args (&airbrush_proc, success);
}

static ProcArg airbrush_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "pressure",
    "The pressure of the airbrush strokes (0 <= pressure <= 100)"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord airbrush_proc =
{
  "gimp_airbrush",
  "Paint in the current brush with varying pressure. Paint application is time-dependent.",
  "This tool simulates the use of an airbrush. Paint pressure represents the relative intensity of the paint application. High pressure results in a thicker layer of paint while low pressure results in a thinner layer.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  4,
  airbrush_inargs,
  0,
  NULL,
  { { airbrush_invoker } }
};

static Argument *
airbrush_default_invoker (Gimp     *gimp,
                          Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = airbrush_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&airbrush_default_proc, success);
}

static ProcArg airbrush_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord airbrush_default_proc =
{
  "gimp_airbrush_default",
  "Paint in the current brush with varying pressure. Paint application is time-dependent.",
  "This tool simulates the use of an airbrush. It is similar to gimp_airbrush except that the pressure is derived from the airbrush tools options box. It the option has not been set the default for the option will be used.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  airbrush_default_inargs,
  0,
  NULL,
  { { airbrush_default_invoker } }
};

static Argument *
blend_invoker (Gimp     *gimp,
               Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 blend_mode;
  gint32 paint_mode;
  gint32 gradient_type;
  gdouble opacity;
  gdouble offset;
  gint32 repeat;
  gboolean supersample;
  gint32 max_depth;
  gdouble threshold;
  gdouble x1;
  gdouble y1;
  gdouble x2;
  gdouble y2;
  GimpImage *gimage;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  blend_mode = args[1].value.pdb_int;
  if (blend_mode < FG_BG_RGB_MODE || blend_mode > CUSTOM_MODE)
    success = FALSE;

  paint_mode = args[2].value.pdb_int;
  if (paint_mode < NORMAL_MODE || paint_mode > HARDLIGHT_MODE)
    success = FALSE;

  gradient_type = args[3].value.pdb_int;
  if (gradient_type < LINEAR || gradient_type > SPIRAL_ANTICLOCKWISE)
    success = FALSE;

  opacity = args[4].value.pdb_float;
  if (opacity < 0.0 || opacity > 100.0)
    success = FALSE;

  offset = args[5].value.pdb_float;
  if (offset < 0.0)
    success = FALSE;

  repeat = args[6].value.pdb_int;
  if (repeat < REPEAT_NONE || repeat > REPEAT_TRIANGULAR)
    success = FALSE;

  supersample = args[7].value.pdb_int ? TRUE : FALSE;

  max_depth = args[8].value.pdb_int;
  if (supersample && (max_depth < 1 || max_depth > 9))
    success = FALSE;

  threshold = args[9].value.pdb_float;
  if (supersample && (threshold < 0.0 || threshold > 4.0))
    success = FALSE;

  x1 = args[10].value.pdb_float;

  y1 = args[11].value.pdb_float;

  x2 = args[12].value.pdb_float;

  y2 = args[13].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
      blend (gimage, drawable, blend_mode, paint_mode, gradient_type, opacity,
	     offset, repeat, supersample, max_depth, threshold, x1, y1, x2, y2,
	     NULL, NULL);
    }

  return procedural_db_return_args (&blend_proc, success);
}

static ProcArg blend_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "blend_mode",
    "The type of blend: { FG_BG_RGB (0), FG_BG_HSV (1), FG_TRANS (2), CUSTOM (3) }"
  },
  {
    GIMP_PDB_INT32,
    "paint_mode",
    "The paint application mode: { NORMAL_MODE (0), DISSOLVE_MODE (1), BEHIND_MODE (2), MULTIPLY_MODE (3), SCREEN_MODE (4), OVERLAY_MODE (5), DIFFERENCE_MODE (6), ADDITION_MODE (7), SUBTRACT_MODE (8), DARKEN_ONLY_MODE (9), LIGHTEN_ONLY_MODE (10), HUE_MODE (11), SATURATION_MODE (12), COLOR_MODE (13), VALUE_MODE (14), DIVIDE_MODE (15), DODGE_MODE (16), BURN_MODE (17), HARDLIGHT_MODE (18) }"
  },
  {
    GIMP_PDB_INT32,
    "gradient_type",
    "The type of gradient: { LINEAR (0), BILINEAR (1), RADIAL (2), SQUARE (3), CONICAL_SYMMETRIC (4), CONICAL_ASYMMETRIC (5), SHAPEBURST_ANGULAR (6), SHAPEBURST_SPHERICAL (7), SHAPEBURST_DIMPLED (8), SPIRAL_CLOCKWISE (9), SPIRAL_ANTICLOCKWISE (10) }"
  },
  {
    GIMP_PDB_FLOAT,
    "opacity",
    "The opacity of the final blend (0 <= opacity <= 100)"
  },
  {
    GIMP_PDB_FLOAT,
    "offset",
    "Offset relates to the starting and ending coordinates specified for the blend. This parameter is mode dependent (0 <= offset)"
  },
  {
    GIMP_PDB_INT32,
    "repeat",
    "Repeat mode: { REPEAT_NONE (0), REPEAT_SAWTOOTH (1), REPEAT_TRIANGULAR (2) }"
  },
  {
    GIMP_PDB_INT32,
    "supersample",
    "Do adaptive supersampling (TRUE or FALSE)"
  },
  {
    GIMP_PDB_INT32,
    "max_depth",
    "Maximum recursion levels for supersampling"
  },
  {
    GIMP_PDB_FLOAT,
    "threshold",
    "Supersampling threshold"
  },
  {
    GIMP_PDB_FLOAT,
    "x1",
    "The x coordinate of this blend's starting point"
  },
  {
    GIMP_PDB_FLOAT,
    "y1",
    "The y coordinate of this blend's starting point"
  },
  {
    GIMP_PDB_FLOAT,
    "x2",
    "The x coordinate of this blend's ending point"
  },
  {
    GIMP_PDB_FLOAT,
    "y2",
    "The y coordinate of this blend's ending point"
  }
};

static ProcRecord blend_proc =
{
  "gimp_blend",
  "Blend between the starting and ending coordinates with the specified blend mode and gradient type.",
  "This tool requires information on the paint application mode, the blend mode, and the gradient type. It creates the specified variety of blend using the starting and ending coordinates as defined for each gradient type.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  14,
  blend_inargs,
  0,
  NULL,
  { { blend_invoker } }
};

static Argument *
bucket_fill_invoker (Gimp     *gimp,
                     Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 fill_mode;
  gint32 paint_mode;
  gdouble opacity;
  gdouble threshold;
  gboolean sample_merged;
  gdouble x;
  gdouble y;
  GimpImage *gimage;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  fill_mode = args[1].value.pdb_int;
  if (fill_mode < FG_BUCKET_FILL || fill_mode > PATTERN_BUCKET_FILL)
    success = FALSE;

  paint_mode = args[2].value.pdb_int;
  if (paint_mode < NORMAL_MODE || paint_mode > HARDLIGHT_MODE)
    success = FALSE;

  opacity = args[3].value.pdb_float;
  if (opacity < 0.0 || opacity > 100.0)
    success = FALSE;

  threshold = args[4].value.pdb_float;
  if (threshold < 0.0 || threshold > 255.0)
    success = FALSE;

  sample_merged = args[5].value.pdb_int ? TRUE : FALSE;

  x = args[6].value.pdb_float;

  y = args[7].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
      bucket_fill (gimage, drawable, fill_mode, paint_mode, opacity,
		   threshold, sample_merged, x, y);
    }

  return procedural_db_return_args (&bucket_fill_proc, success);
}

static ProcArg bucket_fill_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "fill_mode",
    "The type of fill: { FG_BUCKET_FILL (0), BG_BUCKET_FILL (1), PATTERN_BUCKET_FILL (2) }"
  },
  {
    GIMP_PDB_INT32,
    "paint_mode",
    "The paint application mode: { NORMAL_MODE (0), DISSOLVE_MODE (1), BEHIND_MODE (2), MULTIPLY_MODE (3), SCREEN_MODE (4), OVERLAY_MODE (5), DIFFERENCE_MODE (6), ADDITION_MODE (7), SUBTRACT_MODE (8), DARKEN_ONLY_MODE (9), LIGHTEN_ONLY_MODE (10), HUE_MODE (11), SATURATION_MODE (12), COLOR_MODE (13), VALUE_MODE (14), DIVIDE_MODE (15), DODGE_MODE (16), BURN_MODE (17), HARDLIGHT_MODE (18) }"
  },
  {
    GIMP_PDB_FLOAT,
    "opacity",
    "The opacity of the final bucket fill (0 <= opacity <= 100)"
  },
  {
    GIMP_PDB_FLOAT,
    "threshold",
    "The threshold determines how extensive the seed fill will be. It's value is specified in terms of intensity levels (0 <= threshold <= 255). This parameter is only valid when there is no selection in the specified image."
  },
  {
    GIMP_PDB_INT32,
    "sample_merged",
    "Use the composite image, not the drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "x",
    "The x coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image."
  },
  {
    GIMP_PDB_FLOAT,
    "y",
    "The y coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image."
  }
};

static ProcRecord bucket_fill_proc =
{
  "gimp_bucket_fill",
  "Fill the area specified either by the current selection if there is one, or by a seed fill starting at the specified coordinates.",
  "This tool requires information on the paint application mode, and the fill mode, which can either be in the foreground color, or in the currently active pattern. If there is no selection, a seed fill is executed at the specified coordinates and extends outward in keeping with the threshold parameter. If there is a selection in the target image, the threshold, sample merged, x, and y arguments are unused. If the sample_merged parameter is non-zero, the data of the composite image will be used instead of that for the specified drawable. This is equivalent to sampling for colors after merging all visible layers. In the case of merged sampling, the x,y coordinates are relative to the image's origin; otherwise, they are relative to the drawable's origin.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  8,
  bucket_fill_inargs,
  0,
  NULL,
  { { bucket_fill_invoker } }
};

static Argument *
clone_invoker (Gimp     *gimp,
               Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  GimpDrawable *src_drawable;
  gint32 clone_type;
  gdouble src_x;
  gdouble src_y;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  src_drawable = gimp_drawable_get_by_ID (gimp, args[1].value.pdb_int);
  if (src_drawable == NULL)
    success = FALSE;

  clone_type = args[2].value.pdb_int;
  if (clone_type < IMAGE_CLONE || clone_type > PATTERN_CLONE)
    success = FALSE;

  src_x = args[3].value.pdb_float;

  src_y = args[4].value.pdb_float;

  num_strokes = args[5].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[6].value.pdb_pointer;

  if (success)
    success = clone_non_gui (drawable, src_drawable, clone_type, src_x, src_y,
			     num_strokes, strokes);

  return procedural_db_return_args (&clone_proc, success);
}

static ProcArg clone_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_DRAWABLE,
    "src_drawable",
    "The source drawable"
  },
  {
    GIMP_PDB_INT32,
    "clone_type",
    "The type of clone: { IMAGE_CLONE (0), PATTERN_CLONE (1) }"
  },
  {
    GIMP_PDB_FLOAT,
    "src_x",
    "The x coordinate in the source image"
  },
  {
    GIMP_PDB_FLOAT,
    "src_y",
    "The y coordinate in the source image"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord clone_proc =
{
  "gimp_clone",
  "Clone from the source to the dest drawable using the current brush",
  "This tool clones (copies) from the source drawable starting at the specified source coordinates to the dest drawable. If the \"clone_type\" argument is set to PATTERN-CLONE, then the current pattern is used as the source and the \"src_drawable\" argument is ignored. Pattern cloning assumes a tileable pattern and mods the sum of the src coordinates and subsequent stroke offsets with the width and height of the pattern. For image cloning, if the sum of the src coordinates and subsequent stroke offsets exceeds the extents of the src drawable, then no paint is transferred. The clone tool is capable of transforming between any image types including RGB->Indexed--although converting from any type to indexed is significantly slower.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  7,
  clone_inargs,
  0,
  NULL,
  { { clone_invoker } }
};

static Argument *
clone_default_invoker (Gimp     *gimp,
                       Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = clone_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&clone_default_proc, success);
}

static ProcArg clone_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord clone_default_proc =
{
  "gimp_clone_default",
  "Clone from the source to the dest drawable using the current brush",
  "This tool clones (copies) from the source drawable starting at the specified source coordinates to the dest drawable. This function performs exactly the same as the gimp_clone function except that the tools arguments are obtained from the clones option dialog. It this dialog has not been activated then the dialogs default values will be used.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  clone_default_inargs,
  0,
  NULL,
  { { clone_default_invoker } }
};

static Argument *
color_picker_invoker (Gimp     *gimp,
                      Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpImage *gimage;
  GimpDrawable *drawable;
  gdouble x;
  gdouble y;
  gboolean sample_merged;
  gboolean sample_average;
  gdouble average_radius;
  gboolean save_color;
  GimpRGB color;

  gimage = gimp_image_get_by_ID (gimp, args[0].value.pdb_int);
  if (gimage == NULL)
    success = FALSE;

  drawable = gimp_drawable_get_by_ID (gimp, args[1].value.pdb_int);

  x = args[2].value.pdb_float;

  y = args[3].value.pdb_float;

  sample_merged = args[4].value.pdb_int ? TRUE : FALSE;

  sample_average = args[5].value.pdb_int ? TRUE : FALSE;

  average_radius = args[6].value.pdb_float;
  if (sample_average && (average_radius <= 0.0))
    success = FALSE;

  save_color = args[7].value.pdb_int ? TRUE : FALSE;

  if (success)
    {
      if (!sample_merged)
	if (!drawable || (gimp_drawable_gimage (drawable) != gimage))
	  success = FALSE;
    
      if (success)
	success = pick_color (gimage, drawable, (int) x, (int) y,
			      sample_merged, sample_average, average_radius,
			      save_color);
      if (success)
	{
	  gimp_rgba_set_uchar (&color, 
			       col_value[RED_PIX],
			       col_value[GREEN_PIX],
			       col_value[BLUE_PIX],
			       col_value[ALPHA_PIX]);
	}
    }

  return_args = procedural_db_return_args (&color_picker_proc, success);

  if (success)
    return_args[1].value.pdb_color = color;

  return return_args;
}

static ProcArg color_picker_inargs[] =
{
  {
    GIMP_PDB_IMAGE,
    "image",
    "The image"
  },
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "x",
    "x coordinate of upper-left corner of rectangle"
  },
  {
    GIMP_PDB_FLOAT,
    "y",
    "y coordinate of upper-left corner of rectangle"
  },
  {
    GIMP_PDB_INT32,
    "sample_merged",
    "Use the composite image, not the drawable"
  },
  {
    GIMP_PDB_INT32,
    "sample_average",
    "Average the color of all the pixels in a specified radius"
  },
  {
    GIMP_PDB_FLOAT,
    "average_radius",
    "The radius of pixels to average"
  },
  {
    GIMP_PDB_INT32,
    "save_color",
    "Save the color to the active palette"
  }
};

static ProcArg color_picker_outargs[] =
{
  {
    GIMP_PDB_COLOR,
    "color",
    "The return color"
  }
};

static ProcRecord color_picker_proc =
{
  "gimp_color_picker",
  "Determine the color at the given drawable coordinates",
  "This tool determines the color at the specified coordinates. The returned color is an RGB triplet even for grayscale and indexed drawables. If the coordinates lie outside of the extents of the specified drawable, then an error is returned. If the drawable has an alpha channel, the algorithm examines the alpha value of the drawable at the coordinates. If the alpha value is completely transparent (0), then an error is returned. If the sample_merged parameter is non-zero, the data of the composite image will be used instead of that for the specified drawable. This is equivalent to sampling for colors after merging all visible layers. In the case of a merged sampling, the supplied drawable is ignored.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  8,
  color_picker_inargs,
  1,
  color_picker_outargs,
  { { color_picker_invoker } }
};

static Argument *
convolve_invoker (Gimp     *gimp,
                  Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gdouble pressure;
  gint32 convolve_type;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  pressure = args[1].value.pdb_float;
  if (pressure < 0.0 || pressure > 100.0)
    success = FALSE;

  convolve_type = args[2].value.pdb_int;
  if (convolve_type < BLUR_CONVOLVE || convolve_type > SHARPEN_CONVOLVE)
    success = FALSE;

  num_strokes = args[3].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[4].value.pdb_pointer;

  if (success)
    success = convolve_non_gui (drawable, pressure, convolve_type, num_strokes, strokes);

  return procedural_db_return_args (&convolve_proc, success);
}

static ProcArg convolve_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "pressure",
    "The pressure: 0 <= pressure <= 100"
  },
  {
    GIMP_PDB_INT32,
    "convolve_type",
    "Convolve type: { BLUR_CONVOLVE (0), SHARPEN_CONVOLVE (1) }"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord convolve_proc =
{
  "gimp_convolve",
  "Convolve (Blur, Sharpen) using the current brush.",
  "This tool convolves the specified drawable with either a sharpening or blurring kernel. The pressure parameter controls the magnitude of the operation. Like the paintbrush, this tool linearly interpolates between the specified stroke coordinates.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  5,
  convolve_inargs,
  0,
  NULL,
  { { convolve_invoker } }
};

static Argument *
convolve_default_invoker (Gimp     *gimp,
                          Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = convolve_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&convolve_default_proc, success);
}

static ProcArg convolve_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord convolve_default_proc =
{
  "gimp_convolve_default",
  "Convolve (Blur, Sharpen) using the current brush.",
  "This tool convolves the specified drawable with either a sharpening or blurring kernel. This function performs exactly the same as the gimp_convolve function except that the tools arguments are obtained from the convolve option dialog. It this dialog has not been activated then the dialogs default values will be used.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  convolve_default_inargs,
  0,
  NULL,
  { { convolve_default_invoker } }
};

static Argument *
dodgeburn_invoker (Gimp     *gimp,
                   Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gdouble exposure;
  gint32 dodgeburn_type;
  gint32 dodgeburn_mode;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  exposure = args[1].value.pdb_float;
  if (exposure < 0.0 || exposure > 100.0)
    success = FALSE;

  dodgeburn_type = args[2].value.pdb_int;
  if (dodgeburn_type < DODGE || dodgeburn_type > BURN)
    success = FALSE;

  dodgeburn_mode = args[3].value.pdb_int;
  if (dodgeburn_mode < GIMP_SHADOWS || dodgeburn_mode > GIMP_HIGHLIGHTS)
    success = FALSE;

  num_strokes = args[4].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[5].value.pdb_pointer;

  if (success)
    success = gimp_dodgeburn_tool_non_gui (drawable, exposure, dodgeburn_type, dodgeburn_mode, num_strokes, strokes);

  return procedural_db_return_args (&dodgeburn_proc, success);
}

static ProcArg dodgeburn_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "exposure",
    "The exposer of the strokes (0 <= exposure <= 100)"
  },
  {
    GIMP_PDB_INT32,
    "dodgeburn_type",
    "The type either dodge or burn: { DODGE (0), BURN (1) }"
  },
  {
    GIMP_PDB_INT32,
    "dodgeburn_mode",
    "The mode: { SHADOWS (0), MIDTONES (1), HIGHLIGHTS (2) }"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord dodgeburn_proc =
{
  "gimp_dodgeburn",
  "Dodgeburn image with varying exposure.",
  "Dodgebure. More details here later.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  6,
  dodgeburn_inargs,
  0,
  NULL,
  { { dodgeburn_invoker } }
};

static Argument *
dodgeburn_default_invoker (Gimp     *gimp,
                           Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = gimp_dodgeburn_tool_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&dodgeburn_default_proc, success);
}

static ProcArg dodgeburn_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord dodgeburn_default_proc =
{
  "gimp_dodgeburn_default",
  "Dodgeburn image with varying exposure. This is the same as the gimp_dodgeburn function except that the exposure, type and mode are taken from the tools option dialog. If the dialog has not been activated then the defaults as used by the dialog will be used.",
  "Dodgeburn. More details here later.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  3,
  dodgeburn_default_inargs,
  0,
  NULL,
  { { dodgeburn_default_invoker } }
};

static Argument *
eraser_invoker (Gimp     *gimp,
                Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;
  gint32 hardness;
  gint32 method;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  hardness = args[3].value.pdb_int;
  if (hardness < HARD || hardness > SOFT)
    success = FALSE;

  method = args[4].value.pdb_int;
  if (method < CONSTANT || method > INCREMENTAL)
    success = FALSE;

  if (success)
    success = eraser_non_gui (drawable, num_strokes, strokes, hardness, method, TRUE);

  return procedural_db_return_args (&eraser_proc, success);
}

static ProcArg eraser_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  },
  {
    GIMP_PDB_INT32,
    "hardness",
    "HARD (0) or SOFT (1)"
  },
  {
    GIMP_PDB_INT32,
    "method",
    "CONTINUOUS (0) or INCREMENTAL (1)"
  }
};

static ProcRecord eraser_proc =
{
  "gimp_eraser",
  "Erase using the current brush.",
  "This tool erases using the current brush mask. If the specified drawable contains an alpha channel, then the erased pixels will become transparent. Otherwise, the eraser tool replaces the contents of the drawable with the background color. Like paintbrush, this tool linearly interpolates between the specified stroke coordinates.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  5,
  eraser_inargs,
  0,
  NULL,
  { { eraser_invoker } }
};

static Argument *
eraser_default_invoker (Gimp     *gimp,
                        Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = eraser_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&eraser_default_proc, success);
}

static ProcArg eraser_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord eraser_default_proc =
{
  "gimp_eraser_default",
  "Erase using the current brush.",
  "This tool erases using the current brush mask. This function performs exactly the same as the gimp_eraser function except that the tools arguments are obtained from the eraser option dialog. It this dialog has not been activated then the dialogs default values will be used.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  eraser_default_inargs,
  0,
  NULL,
  { { eraser_default_invoker } }
};

static Argument *
flip_invoker (Gimp     *gimp,
              Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gint32 flip_type;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  flip_type = args[1].value.pdb_int;
  if (flip_type < HORIZONTAL || flip_type > VERTICAL)
    success = FALSE;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
    
      /* Start a transform undo group */
      undo_push_group_start (gimage, TRANSFORM_CORE_UNDO); 
    
      /* Cut/Copy from the specified drawable */
      float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
      flip_type = flip_type == HORIZONTAL ? ORIENTATION_HORIZONTAL :
		  flip_type == VERTICAL   ? ORIENTATION_VERTICAL   :
		  ORIENTATION_UNKNOWN;
    
      /* flip the buffer */
      switch (flip_type)
	{
	case ORIENTATION_HORIZONTAL:
	case ORIENTATION_VERTICAL:
	  new_tiles = flip_tool_flip (gimage, drawable, float_tiles, -1, flip_type);
	  break;
	default:
	  new_tiles = NULL;
	  break;
	}
    
      /* free the cut/copied buffer */
      tile_manager_destroy (float_tiles);
    
      if (new_tiles)
	success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
      else
	success = FALSE;
    
      /* push the undo group end  */
      undo_push_group_end (gimage);
    }

  return_args = procedural_db_return_args (&flip_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg flip_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "flip_type",
    "Type of flip: HORIZONTAL (0) or VERTICAL (1)"
  }
};

static ProcArg flip_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The flipped drawable"
  }
};

static ProcRecord flip_proc =
{
  "gimp_flip",
  "Flip the specified drawable about its center either vertically or horizontally.",
  "This tool flips the specified drawable if no selection exists. If a selection exists, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then flipd by the specified amount. The return value is the ID of the flipped drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and flipped drawable. The flip type parameter indicates whether the flip will be applied horizontally or vertically.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  2,
  flip_inargs,
  1,
  flip_outargs,
  { { flip_invoker } }
};

static Argument *
paintbrush_invoker (Gimp     *gimp,
                    Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gdouble fade_out;
  gint32 num_strokes;
  gdouble *strokes;
  gint32 method;
  gdouble gradient_length;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  fade_out = args[1].value.pdb_float;
  if (fade_out < 0.0)
    success = FALSE;

  num_strokes = args[2].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[3].value.pdb_pointer;

  method = args[4].value.pdb_int;
  if (method < CONSTANT || method > INCREMENTAL)
    success = FALSE;

  gradient_length = args[5].value.pdb_float;
  if (gradient_length < 0.0)
    success = FALSE;

  if (success)
    success = gimp_paintbrush_tool_non_gui (drawable, num_strokes, strokes, 
					    fade_out, method, gradient_length);

  return procedural_db_return_args (&paintbrush_proc, success);
}

static ProcArg paintbrush_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "fade_out",
    "Fade out parameter: 0 <= fade_out"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  },
  {
    GIMP_PDB_INT32,
    "method",
    "CONTINUOUS (0) or INCREMENTAL (1)"
  },
  {
    GIMP_PDB_FLOAT,
    "gradient_length",
    "Length of gradient to draw: 0 <= gradient_length"
  }
};

static ProcRecord paintbrush_proc =
{
  "gimp_paintbrush",
  "Paint in the current brush with optional fade out parameter and pull colors from a gradient.",
  "This tool is the standard paintbrush. It draws linearly interpolated lines through the specified stroke coordinates. It operates on the specified drawable in the foreground color with the active brush. The \"fade_out\" parameter is measured in pixels and allows the brush stroke to linearly fall off. The pressure is set to the maximum at the beginning of the stroke. As the distance of the stroke nears the fade_out value, the pressure will approach zero. The gradient_length is the distance to spread the gradient over. It is measured in pixels. If the gradient_length is 0, no gradient is used.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  6,
  paintbrush_inargs,
  0,
  NULL,
  { { paintbrush_invoker } }
};

static Argument *
paintbrush_default_invoker (Gimp     *gimp,
                            Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = gimp_paintbrush_tool_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&paintbrush_default_proc, success);
}

static ProcArg paintbrush_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord paintbrush_default_proc =
{
  "gimp_paintbrush_default",
  "Paint in the current brush. The fade out parameter and pull colors from a gradient parameter are set from the paintbrush options dialog. If this dialog has not been activated then the dialog defaults will be used.",
  "This tool is similar to the standard paintbrush. It draws linearly interpolated lines through the specified stroke coordinates. It operates on the specified drawable in the foreground color with the active brush. The \"fade_out\" parameter is measured in pixels and allows the brush stroke to linearly fall off (value obtained from the option dialog). The pressure is set to the maximum at the beginning of the stroke. As the distance of the stroke nears the fade_out value, the pressure will approach zero. The gradient_length (value obtained from the option dialog) is the distance to spread the gradient over. It is measured in pixels. If the gradient_length is 0, no gradient is used.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  paintbrush_default_inargs,
  0,
  NULL,
  { { paintbrush_default_invoker } }
};

static Argument *
pencil_invoker (Gimp     *gimp,
                Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = pencil_non_gui (drawable, num_strokes, strokes);

  return procedural_db_return_args (&pencil_proc, success);
}

static ProcArg pencil_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord pencil_proc =
{
  "gimp_pencil",
  "Paint in the current brush without sub-pixel sampling.",
  "This tool is the standard pencil. It draws linearly interpolated lines through the specified stroke coordinates. It operates on the specified drawable in the foreground color with the active brush. The brush mask is treated as though it contains only black and white values. Any value below half is treated as black; any above half, as white.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  3,
  pencil_inargs,
  0,
  NULL,
  { { pencil_invoker } }
};

static Argument *
perspective_invoker (Gimp     *gimp,
                     Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gboolean interpolation;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;
  gint offset_x, offset_y;
  gdouble cx, cy;
  gdouble scalex, scaley;
  gdouble trans_info[8];
  GimpMatrix3 m, matrix;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  interpolation = args[1].value.pdb_int ? TRUE : FALSE;

  trans_info[X0] = args[2].value.pdb_float;

  trans_info[Y0] = args[3].value.pdb_float;

  trans_info[X1] = args[4].value.pdb_float;

  trans_info[Y1] = args[5].value.pdb_float;

  trans_info[X2] = args[6].value.pdb_float;

  trans_info[Y2] = args[7].value.pdb_float;

  trans_info[X3] = args[8].value.pdb_float;

  trans_info[Y3] = args[9].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
    
      /* Start a transform undo group */
      undo_push_group_start (gimage, TRANSFORM_CORE_UNDO);
    
      /* Cut/Copy from the specified drawable */
      float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
      /* Determine the perspective transform that maps from
       * the unit cube to the trans_info coordinates
       */
      gimp_perspective_tool_find_transform (trans_info, m);
    
      tile_manager_get_offsets (float_tiles, &offset_x, &offset_y);        
      cx = (gdouble) offset_x;
      cy = (gdouble) offset_y;                
      scalex = 1.0;
      scaley = 1.0;
      if (tile_manager_width (float_tiles))
	scalex = 1.0 / tile_manager_width (float_tiles);
      if (tile_manager_height (float_tiles))
	scaley = 1.0 / tile_manager_height (float_tiles);
    
      /* Assemble the transformation matrix */
      gimp_matrix3_identity  (matrix);
      gimp_matrix3_translate (matrix, -cx, -cy);
      gimp_matrix3_scale     (matrix, scalex, scaley);
      gimp_matrix3_mult      (m, matrix);
    
      /* Perspective the buffer */
      new_tiles = gimp_perspective_tool_perspective (gimage, drawable, NULL,
						float_tiles, interpolation,
						matrix);
    
      /* Free the cut/copied buffer */
      tile_manager_destroy (float_tiles);
    
      if (new_tiles)
	success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
      else
	success = FALSE;
    
      /*  push the undo group end  */
      undo_push_group_end (gimage);
    }

  return_args = procedural_db_return_args (&perspective_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg perspective_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "interpolation",
    "Whether to use interpolation"
  },
  {
    GIMP_PDB_FLOAT,
    "x0",
    "The new x coordinate of upper-left corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "y0",
    "The new y coordinate of upper-left corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "x1",
    "The new x coordinate of upper-right corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "y1",
    "The new y coordinate of upper-right corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "x2",
    "The new x coordinate of lower-left corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "y2",
    "The new y coordinate of lower-left corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "x3",
    "The new x coordinate of lower-right corner of original bounding box"
  },
  {
    GIMP_PDB_FLOAT,
    "y3",
    "The new y coordinate of lower-right corner of original bounding box"
  }
};

static ProcArg perspective_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The newly mapped drawable"
  }
};

static ProcRecord perspective_proc =
{
  "gimp_perspective",
  "Perform a possibly non-affine transformation on the specified drawable.",
  "This tool performs a possibly non-affine transformation on the specified drawable by allowing the corners of the original bounding box to be arbitrarily remapped to any values. The specified drawable is remapped if no selection exists. However, if a selection exists, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then remapped as specified. The interpolation parameter can be set to TRUE to indicate that either linear or cubic interpolation should be used to smooth the resulting remapped drawable. The return value is the ID of the remapped drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and remapped drawable. 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 drawable will appear to have been projected via a perspective transform.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  10,
  perspective_inargs,
  1,
  perspective_outargs,
  { { perspective_invoker } }
};

static Argument *
rotate_invoker (Gimp     *gimp,
                Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gboolean interpolation;
  gdouble angle;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;
  gint offset_x, offset_y;
  gdouble cx, cy;
  GimpMatrix3 matrix;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  interpolation = args[1].value.pdb_int ? TRUE : FALSE;

  angle = args[2].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
    
      /* Start a transform undo group */
      undo_push_group_start (gimage, TRANSFORM_CORE_UNDO);
    
      /* Cut/Copy from the specified drawable */
      float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
      tile_manager_get_offsets (float_tiles, &offset_x, &offset_y);        
      cx = offset_x + tile_manager_width (float_tiles) / 2.0;
      cy = offset_y + tile_manager_height (float_tiles) / 2.0;
    
      /* Assemble the transformation matrix */
      gimp_matrix3_identity  (matrix);
      gimp_matrix3_translate (matrix, -cx, -cy);
      gimp_matrix3_rotate    (matrix, angle);
      gimp_matrix3_translate (matrix, +cx, +cy);
    
      /* Rotate the buffer */
      new_tiles = gimp_rotate_tool_rotate (gimage, drawable, NULL, angle,
				      float_tiles, interpolation, matrix);
    
      /* Free the cut/copied buffer */
      tile_manager_destroy (float_tiles);
    
      if (new_tiles)
	success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
      else
	success = FALSE;
    
      /* Push the undo group end */
      undo_push_group_end (gimage);
    }

  return_args = procedural_db_return_args (&rotate_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg rotate_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "interpolation",
    "Whether to use interpolation"
  },
  {
    GIMP_PDB_FLOAT,
    "angle",
    "The angle of rotation (radians)"
  }
};

static ProcArg rotate_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The rotated drawable"
  }
};

static ProcRecord rotate_proc =
{
  "gimp_rotate",
  "Rotate the specified drawable about its center through the specified angle.",
  "This tool rotates the specified drawable if no selection exists. If a selection exists, 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. The interpolation parameter can be set to TRUE to indicate that either linear or cubic interpolation should be used to smooth the resulting rotated drawable. The return value is the ID of the rotated drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and rotated drawable.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  3,
  rotate_inargs,
  1,
  rotate_outargs,
  { { rotate_invoker } }
};

static Argument *
scale_invoker (Gimp     *gimp,
               Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gboolean interpolation;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;
  gint offset_x, offset_y;
  gdouble scalex, scaley;
  gdouble trans_info[4];
  GimpMatrix3 matrix;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  interpolation = args[1].value.pdb_int ? TRUE : FALSE;

  trans_info[X0] = args[2].value.pdb_float;

  trans_info[Y0] = args[3].value.pdb_float;

  trans_info[X1] = args[4].value.pdb_float;

  trans_info[Y1] = args[5].value.pdb_float;

  if (success)
    {
      if (trans_info[X0] < trans_info[X1] &&
	  trans_info[Y0] < trans_info[X1])
	{
	  gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
    
	  /* Start a transform undo group */
	  undo_push_group_start (gimage, TRANSFORM_CORE_UNDO);
    
	  /* Cut/Copy from the specified drawable */
	  float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
	  scalex = scaley = 1.0;
	  if (tile_manager_width (float_tiles))
	    scalex = (trans_info[X1] - trans_info[X0]) /
		     (gdouble) tile_manager_width (float_tiles);
	  if (tile_manager_height (float_tiles))
	    scaley = (trans_info[Y1] - trans_info[Y0]) /
		     (gdouble) tile_manager_height (float_tiles);
    
	  tile_manager_get_offsets (float_tiles, &offset_x, &offset_y);        
    
	  /* Assemble the transformation matrix */
	  gimp_matrix3_identity  (matrix);
	  gimp_matrix3_translate (matrix, offset_x, offset_y);
	  gimp_matrix3_scale     (matrix, scalex, scaley);
	  gimp_matrix3_translate (matrix, trans_info[X0], trans_info[Y0]);
    
	  /* Scale the buffer */
	  new_tiles = gimp_scale_tool_scale (gimage, drawable, NULL, trans_info,
					     float_tiles, interpolation, matrix);
    
	  /* Free the cut/copied buffer */
	  tile_manager_destroy (float_tiles);
    
	  if (new_tiles)
	    success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
	  else
	    success = FALSE;
    
	  /*  push the undo group end  */
	  undo_push_group_end (gimage);
	}
      else
	success = FALSE;
    }

  return_args = procedural_db_return_args (&scale_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg scale_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "interpolation",
    "Whether to use interpolation"
  },
  {
    GIMP_PDB_FLOAT,
    "x0",
    "The new x coordinate of upper-left corner of newly scaled region"
  },
  {
    GIMP_PDB_FLOAT,
    "y0",
    "The new y coordinate of upper-left corner of newly scaled region"
  },
  {
    GIMP_PDB_FLOAT,
    "x1",
    "The new x coordinate of lower-right corner of newly scaled region"
  },
  {
    GIMP_PDB_FLOAT,
    "y1",
    "The new y coordinate of lower-right corner of newly scaled region"
  }
};

static ProcArg scale_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The scaled drawable"
  }
};

static ProcRecord scale_proc =
{
  "gimp_scale",
  "Scale the specified drawable.",
  "This tool scales the specified drawable if no selection exists. If a selection exists, 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 by the specified amount. The interpolation parameter can be set to TRUE to indicate that either linear or cubic interpolation should be used to smooth the resulting scaled drawable. The return value is the ID of the scaled drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and scaled drawable.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  6,
  scale_inargs,
  1,
  scale_outargs,
  { { scale_invoker } }
};

static Argument *
shear_invoker (Gimp     *gimp,
               Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gboolean interpolation;
  gint32 shear_type;
  gdouble magnitude;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;
  gdouble cx, cy;
  gint offset_x, offset_y;
  GimpMatrix3 matrix;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  interpolation = args[1].value.pdb_int ? TRUE : FALSE;

  shear_type = args[2].value.pdb_int;
  if (shear_type < HORIZONTAL || shear_type > VERTICAL)
    success = FALSE;

  magnitude = args[3].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
    
      /* Start a transform undo group */
      undo_push_group_start (gimage, TRANSFORM_CORE_UNDO);
    
      /* Cut/Copy from the specified drawable */
      float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
      tile_manager_get_offsets (float_tiles, &offset_x, &offset_y);        
    
      cx = offset_x + tile_manager_width  (float_tiles) / 2.0;
      cy = offset_y + tile_manager_height (float_tiles) / 2.0;
    
      gimp_matrix3_identity  (matrix);
      gimp_matrix3_translate (matrix, -cx, -cy);
      /* Shear matrix */
      shear_type = shear_type == HORIZONTAL ? ORIENTATION_HORIZONTAL :
		   shear_type == VERTICAL   ? ORIENTATION_VERTICAL   :
		   ORIENTATION_UNKNOWN;
    
      if (shear_type == ORIENTATION_HORIZONTAL)
	gimp_matrix3_xshear (matrix, magnitude / tile_manager_height (float_tiles));
      else if (shear_type == ORIENTATION_VERTICAL)
	gimp_matrix3_yshear (matrix, magnitude / tile_manager_width (float_tiles));
    
      gimp_matrix3_translate (matrix, +cx, +cy);
    
      /* Shear the buffer */
      new_tiles = gimp_shear_tool_shear (gimage, drawable, NULL, float_tiles,
					 interpolation, matrix);
    
      /* Free the cut/copied buffer */
      tile_manager_destroy (float_tiles);
    
      if (new_tiles)
	success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
      else
	success = FALSE;
    
      /* Push the undo group end */
      undo_push_group_end (gimage);
    }

  return_args = procedural_db_return_args (&shear_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg shear_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "interpolation",
    "Whether to use interpolation"
  },
  {
    GIMP_PDB_INT32,
    "shear_type",
    "Type of shear: HORIZONTAL (0) or VERTICAL (1)"
  },
  {
    GIMP_PDB_FLOAT,
    "magnitude",
    "The magnitude of the shear"
  }
};

static ProcArg shear_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The sheared drawable"
  }
};

static ProcRecord shear_proc =
{
  "gimp_shear",
  "Shear the specified drawable about its center by the specified magnitude.",
  "This tool shears the specified drawable if no selection exists. If a selection exists, the portion of the drawable which lies under the selection is cut from the drawable and made into a floating selection which is then sheard by the specified amount. The interpolation parameter can be set to TRUE to indicate that either linear or cubic interpolation should be used to smooth the resulting sheared drawable. The return value is the ID of the sheard drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and sheard drawable. 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.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  4,
  shear_inargs,
  1,
  shear_outargs,
  { { shear_invoker } }
};

static Argument *
smudge_invoker (Gimp     *gimp,
                Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gdouble pressure;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  pressure = args[1].value.pdb_float;
  if (pressure < 0.0 || pressure > 100.0)
    success = FALSE;

  num_strokes = args[2].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[3].value.pdb_pointer;

  if (success)
    success = gimp_smudge_tool_non_gui (drawable, pressure, num_strokes, strokes);

  return procedural_db_return_args (&smudge_proc, success);
}

static ProcArg smudge_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_FLOAT,
    "pressure",
    "The pressure of the smudge strokes (0 <= pressure <= 100)"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord smudge_proc =
{
  "gimp_smudge",
  "Smudge image with varying pressure.",
  "This tool simulates a smudge using the current brush. High pressure results in a greater smudge of paint while low pressure results in a lesser smudge.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  4,
  smudge_inargs,
  0,
  NULL,
  { { smudge_invoker } }
};

static Argument *
smudge_default_invoker (Gimp     *gimp,
                        Argument *args)
{
  gboolean success = TRUE;
  GimpDrawable *drawable;
  gint32 num_strokes;
  gdouble *strokes;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  num_strokes = args[1].value.pdb_int;
  if (!(num_strokes < 2))
    num_strokes /= 2;
  else
    success = FALSE;

  strokes = (gdouble *) args[2].value.pdb_pointer;

  if (success)
    success = gimp_smudge_tool_non_gui_default (drawable, num_strokes, strokes);

  return procedural_db_return_args (&smudge_default_proc, success);
}

static ProcArg smudge_default_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "num_strokes",
    "Number of stroke control points (count each coordinate as 2 points)"
  },
  {
    GIMP_PDB_FLOATARRAY,
    "strokes",
    "Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }"
  }
};

static ProcRecord smudge_default_proc =
{
  "gimp_smudge_default",
  "Smudge image with varying pressure.",
  "This tool simulates a smudge using the current brush. It behaves exactly the same as gimp_smudge except that the pressure value is taken from the smudge tool options or the options default if the tools option dialog has not been activated.",
  "Andy Thomas",
  "Andy Thomas",
  "1999",
  GIMP_INTERNAL,
  3,
  smudge_default_inargs,
  0,
  NULL,
  { { smudge_default_invoker } }
};

static Argument *
transform_2d_invoker (Gimp     *gimp,
                      Argument *args)
{
  gboolean success = TRUE;
  Argument *return_args;
  GimpDrawable *drawable;
  gboolean interpolation;
  gdouble source_x;
  gdouble source_y;
  gdouble scale_x;
  gdouble scale_y;
  gdouble angle;
  gdouble dest_x;
  gdouble dest_y;
  GimpImage *gimage;
  TileManager *float_tiles, *new_tiles;
  gboolean new_layer;
  GimpMatrix3 matrix;

  drawable = gimp_drawable_get_by_ID (gimp, args[0].value.pdb_int);
  if (drawable == NULL)
    success = FALSE;

  interpolation = args[1].value.pdb_int ? TRUE : FALSE;

  source_x = args[2].value.pdb_float;

  source_y = args[3].value.pdb_float;

  scale_x = args[4].value.pdb_float;

  scale_y = args[5].value.pdb_float;

  angle = args[6].value.pdb_float;

  dest_x = args[7].value.pdb_float;

  dest_y = args[8].value.pdb_float;

  if (success)
    {
      gimage = gimp_drawable_gimage (GIMP_DRAWABLE (drawable));
	
      /* Start a transform undo group */
      undo_push_group_start (gimage, TRANSFORM_CORE_UNDO);
	
      /* Cut/Copy from the specified drawable */
      float_tiles = gimp_transform_tool_cut (gimage, drawable, &new_layer);
    
      /* 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);
	
      /* Transform the buffer */
      new_tiles = gimp_transform_tool_do (gimage, drawable, float_tiles,
					  interpolation, matrix, NULL, NULL);
	
      /* Free the cut/copied buffer */
      tile_manager_destroy (float_tiles);
	
      if (new_tiles)
	success = gimp_transform_tool_paste (gimage, drawable, new_tiles, new_layer);
      else
	success = FALSE;
    
      /* Push the undo group end */
      undo_push_group_end (gimage);
    }

  return_args = procedural_db_return_args (&transform_2d_proc, success);

  if (success)
    return_args[1].value.pdb_int = gimp_drawable_get_ID (GIMP_DRAWABLE (drawable));

  return return_args;
}

static ProcArg transform_2d_inargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The affected drawable"
  },
  {
    GIMP_PDB_INT32,
    "interpolation",
    "Whether to use interpolation"
  },
  {
    GIMP_PDB_FLOAT,
    "source_x",
    "X coordinate of the transformation center"
  },
  {
    GIMP_PDB_FLOAT,
    "source_y",
    "Y coordinate of the transformation center"
  },
  {
    GIMP_PDB_FLOAT,
    "scale_x",
    "Amount to scale in x direction"
  },
  {
    GIMP_PDB_FLOAT,
    "scale_y",
    "Amount to scale in y direction"
  },
  {
    GIMP_PDB_FLOAT,
    "angle",
    "The angle of rotation (radians)"
  },
  {
    GIMP_PDB_FLOAT,
    "dest_x",
    "X coordinate of where the centre goes"
  },
  {
    GIMP_PDB_FLOAT,
    "dest_y",
    "Y coordinate of where the centre goes"
  }
};

static ProcArg transform_2d_outargs[] =
{
  {
    GIMP_PDB_DRAWABLE,
    "drawable",
    "The transformed drawable"
  }
};

static ProcRecord transform_2d_proc =
{
  "gimp_transform_2d",
  "Transform the specified drawable in 2d.",
  "This tool transforms the specified drawable if no selection exists. If a selection exists, 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. The interpolation parameter can be set to TRUE to indicate that either linear or cubic interpolation should be used to smooth the resulting drawable. The transformation is done by scaling the image 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). The return value is the ID of the rotated drawable. If there was no selection, this will be equal to the drawable ID supplied as input. Otherwise, this will be the newly created and transformed drawable.",
  "Spencer Kimball & Peter Mattis",
  "Spencer Kimball & Peter Mattis",
  "1995-1996",
  GIMP_INTERNAL,
  9,
  transform_2d_inargs,
  1,
  transform_2d_outargs,
  { { transform_2d_invoker } }
};
