# The GIMP -- an image manipulation program
# Copyright (C) 1995 Spencer Kimball and Peter Mattis

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 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.

# "Perlized" from C source by Manish Singh <yosh@gimp.org>

# Tools

# shortcuts

sub drawable_arg () {{
    name => 'drawable',
    type => 'drawable',
    desc => 'The affected drawable',
}}

sub drawable_out_arg {
    my $type = shift;
    my $arg = &drawable_arg;
    $arg->{desc} = "The $type drawable";
    $arg->{no_declare} = 1;
    $arg;
}

sub sample_merged_arg () {{
    name => 'sample_merged',
    type => 'boolean',
    desc => 'Use the composite image, not the drawable'
}}

sub operation_arg () {{
    name => 'operation',
    type => 'enum ChannelOps',
    desc => 'The selection operation: { %%desc%% }'
}}

sub threshold_arg () {{
    name => 'threshold',
    type => '0 <= int32 <= 255',
    desc => 'Threshold in intensity levels %%desc%%'
}}

sub feather_select_args () {(
    { name => 'feather', type => 'boolean',
      desc => 'Feather option for selections' },
    { name => 'feather_radius', type => 'float',
      desc => 'Radius for feather operation' }
)}

sub stroke_arg () {
    { name => 'strokes', type => 'floatarray',
      desc => 'Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ...,
	       sn.x, sn.y }',
      array => { type => '2 <= int32', on_success => 'num_strokes /= 2;',
		 desc => 'Number of stroke control points (count each
			  coordinate as 2 points)' } }
}

# The defs

sub airbrush {
    $blurb  = <<'BLURB';
Paint in the current brush with varying pressure. Paint application is
time-dependent.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	 { name => 'pressure', type => '0 <= float <= 100',
	   desc => 'The pressure of the airbrush strokes (%%desc%%)' },
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("airbrush.h") ],
	code => <<'CODE'
success = airbrush_non_gui (drawable, pressure, num_strokes, strokes);
CODE
    );
}

sub airbrush_default {
    $blurb  = <<'BLURB';
Paint in the current brush with varying pressure. Paint application is
time-dependent.
BLURB

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("airbrush.h") ],
	code => <<'CODE'
success = airbrush_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}


sub blend {
    $blurb = <<'BLURB';
Blend between the starting and ending coordinates with the specified blend mode
and gradient type.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
        { name => 'blend_mode', type => 'enum BlendMode',
	  desc => 'The type of blend: { %%desc%% }' },
	{ name => 'paint_mode', type => 'enum LayerModeEffects',
	  desc => 'The paint application mode: { %%desc%% }' },
	{ name => 'gradient_type',  type => 'enum GradientType',
	  desc => 'The type of gradient: { %%desc%% }' },
	{ name => 'opacity', type => '0 <= float <= 100',
	  desc => 'The opacity of the final blend (%%desc%%)' },
	{ name => 'offset', type => '0 <= float',
	  desc => 'Offset relates to the starting and ending coordinates
		   specified for the blend. This parameter is mode dependent
		   (%%desc%%)' },
	{ name => 'repeat', type => 'enum RepeatMode',
	  desc => 'Repeat mode: { %%desc%% }' },
	{ name => 'supersample', type => 'boolean',
	  desc => 'Do adaptive supersampling (%%desc%%)' },
	{ name => 'max_depth', type => '1 <= int32 <= 9',
	  desc => 'Maximum recursion levels for supersampling',
	  cond => [ 'supersample' ] },
	{ name => 'threshold', type => '0 <= float <= 4',
	  desc => 'Supersampling threshold',
	  cond => [ 'supersample' ] },
	{ name => 'x1', type => 'float',
	  desc => "The x coordinate of this blend's starting point" },
	{ name => 'y1', type => 'float',
	  desc => "The y coordinate of this blend's starting point" },
	{ name => 'x2', type => 'float',
	  desc => "The x coordinate of this blend's ending point" },
	{ name => 'y2', type => 'float',
	  desc => "The y coordinate of this blend's ending point" }
    );

    %invoke = (
        headers => [ qw("blend.h") ],
	vars => [ 'GimpImage *gimage' ],
	code => <<'CODE'
{
  gimage = 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);
}
CODE
    );
}

sub bucket_fill {
    $blurb = <<'BLURB';
Fill the area specified either by the current selection if there is one, or by
a seed fill starting at the specified coordinates.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    my $validity = 'This parameter is only valid when there is no selection in
		    the specified image.';

    @inargs = (
	&drawable_arg,
	{ name => 'fill_mode', type => 'enum BucketFillMode',
	  desc => 'The type of fill: { %%desc%% }' },
	{ name => paint_mode, type => 'enum LayerModeEffects',
	  desc => 'The paint application mode: { %%desc%% }' },
	{ name => 'opacity', type => '0 <= float <= 100',
	  desc => 'The opacity of the final bucket fill (%%desc%%)' },
	{ name => 'threshold', type => '0 <= float <= 255',
	  desc => "The threshold determines how extensive the seed fill will
		   be. It's value is specified in terms of intensity levels
		   (%%desc%%). $validity" },
	&sample_merged_arg,
    );

    foreach (qw(x y)) {
	push @inargs, { name => $_, type => 'float',
			desc => "The $_ coordinate of this bucket fill's
				 application. $validity" }
    }

    %invoke = (
        headers => [ qw ("bucket_fill.h") ],
	vars => [ 'GimpImage *gimage' ],
	code => <<'CODE'
{
  gimage = drawable_gimage (GIMP_DRAWABLE (drawable));
  bucket_fill (gimage, drawable, fill_mode, paint_mode, opacity,
	       threshold, sample_merged, x, y);
}
CODE
    );
}

sub by_color_select {
    $blurb = <<'BLURB';
Create a selection by selecting all pixels (in the specified drawable) with the
same (or similar) color to that specified.
BLURB

    $help = <<'HELP';
This tool creates a selection over the specified image. A by-color selection is
determined by the supplied color under the constraints of the specified
threshold. Essentially, all pixels (in the drawable) that have color
sufficiently close to the specified color (as determined by the threshold
value) are included in the selection. The antialiasing parameter allows the
final selection mask to contain intermediate values based on close misses to
the threshold bar. Feathering can be enabled optionally and is controlled with
the "feather_radius" parameter. 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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'color', type => 'color',
	  desc => 'The color to select' },
	&threshold_arg,
	&operation_arg,
	&std_antialias_arg,
	&feather_select_args,
	&sample_merged_arg
    );

    %invoke = (
        headers => [ qw("by_color_select.h") ],
	vars => [ 'GimpImage *gimage' ],
	code => <<'CODE'
{
  gimage = drawable_gimage (GIMP_DRAWABLE (drawable));
  by_color_select (gimage, drawable, color, threshold, operation,
		   antialias, feather, feather_radius, sample_merged);
}
CODE
    );
}

sub clone {
    $blurb = <<'BLURB';
Clone from the source to the dest drawable using the current brush
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'src_drawable', type => 'drawable',
	  desc => 'The source drawable' },
	{ name => 'clone_type', type => 'enum CloneType',
	  desc => 'The type of clone: { %%desc%% }' },
	{ name => 'src_x', type => 'float',
	  desc => 'The x coordinate in the source image' },
	{ name => 'src_y', type => 'float',
	  desc => 'The y coordinate in the source image' },
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("clone.h") ],
	code => <<'CODE'
success = clone_non_gui (drawable, src_drawable, clone_type, src_x, src_y,
			 num_strokes, strokes);
CODE
    );
}

sub clone_default {
    $blurb = <<'BLURB';
Clone from the source to the dest drawable using the current brush
BLURB

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("clone.h") ],
	code => <<'CODE'
success = clone_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}

sub color_picker {
    $blurb = <<'BLURB';
Determine the color at the given drawable coordinates
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&std_image_arg,
	&drawable_arg,
	{ name => 'x', type => 'float',
	  desc => 'x coordinate of upper-left corner of rectangle' },
	{ name => 'y', type => 'float',
	  desc => 'y coordinate of upper-left corner of rectangle' },
	&sample_merged_arg,
	{ name => 'sample_average', type => 'boolean',
	  desc => 'Average the color of all the pixels in a specified
		   radius' },
	{ name => 'average_radius', type => '0 < float',
	  desc => 'The radius of pixels to average',
	  cond => [ 'sample_average' ] },
	{ name => 'save_color', type => 'boolean',
	  desc => 'Save the color to the active palette' }
    );
    $inargs[1]->{no_success} = 1;

    @outargs = (
	{ name => 'color', type => 'color',
	  desc => 'The return color', init => 1 }
    );

    %invoke = (
	headers => [ qw("color_picker.h") ],
	code => <<'CODE'
{
  if (!sample_merged)
    if (!drawable || (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)
    {
      color = g_new (guchar, 3);
      color[RED_PIX] = col_value[RED_PIX];
      color[GREEN_PIX] = col_value[GREEN_PIX];
      color[BLUE_PIX] = col_value[BLUE_PIX];
    }
}
CODE
    );
}

sub convolve_default {
    $blurb = 'Convolve (Blur, Sharpen) using the current brush.';

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("convolve.h") ],
	code => <<'CODE'
success = convolve_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}

sub convolve {
    $blurb = 'Convolve (Blur, Sharpen) using the current brush.';

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'pressure', type => '0 <= float <= 100',
	  desc => 'The pressure: %%desc%%' },
	{ name => 'convolve_type',
	  type => 'enum ConvolveType (no CUSTOM_CONVOLVE)',
	  desc => 'Convolve type: { %%desc%% }' },
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("convolve.h") ],
	code => <<'CODE'
success = convolve_non_gui (drawable, pressure, convolve_type, num_strokes, strokes);
CODE
    );
}

sub crop {
    $blurb = 'Crop the image to the specified extents.';

    $help = <<'HELP';
This procedure crops the image so that it's new width and height are equal to
the supplied parameters. Offsets are also provided which describe the position
of the previous image's content. All channels and layers within the image are 
cropped to the new image extents; this includes the image selection mask. If
any parameters are out of range, an error is returned.
HELP

    &std_pdb_misc;

    @inargs = (
	&std_image_arg,
	{ name => 'new_width', type => '0 < int32',
	  desc => 'New image width: (0 < new_width <= width)' },
	{ name => 'new_height', type => '0 < int32',
	  desc => 'New image height: (0 < new_height <= height)' },
	{ name => 'offx', type => '0 <= int32',
	  desc => 'x offset: (0 <= offx <= (width - new_width))' },
	{ name => 'offy', type => '0 <= int32',
	  desc => 'y offset: (0 <= offy <= (height - new_height))' }
    );

    %invoke = (
	headers => [ qw("crop.h") ],
	code => <<'CODE'
{
  if (new_width > gimage->width || new_height > gimage->height ||
      offx > (gimage->width - new_width) ||
      offy > (gimage->height - new_height))
    success = FALSE;
  else
    crop_image (gimage, offx, offy, offx + new_width, offy + new_height,
		FALSE, TRUE);
}
CODE
    );
}

sub ellipse_select {
    $blurb = 'Create an elliptical selection over the specified image.';

    $help = <<'HELP';
This tool creates an elliptical selection over the specified image. The
elliptical region can be either added to, subtracted from, or replace the
contents of the previous selection mask. If antialiasing is turned on, the
edges of the elliptical region will contain intermediate values which give the
appearance of a sharper, less pixelized edge. This should be set as TRUE most
of the time. If the feather option is enabled, the resulting selection is
blurred before combining. The blur is a gaussian blur with the specified
feather radius.
HELP

    &std_pdb_misc;

    @inargs = (
	&std_image_arg,
	{ name => 'x', type => 'float',
	  desc => 'x coordinate of upper-left corner of ellipse bounding box' },
	{ name => 'y', type => 'float',
	  desc => 'y coordinate of upper-left corner of ellipse bounding box' },
	{ name => 'width', type => '0 < float',
	  desc => 'The width of the ellipse: %%desc%%' },
	{ name => 'height', type => '0 < float',
	  desc => 'The height of the ellipse: %%desc%%' },
	&operation_arg,
	&std_antialias_arg,
	&feather_select_args
    );

    %invoke = (
	headers => [ qw("ellipse_select.h") ],
	code => <<'CODE'
ellipse_select (gimage, (int) x, (int) y, (int) width, (int) height,
		operation, antialias, feather, feather_radius);
CODE
    );
}

sub eraser_default {
    $blurb = 'Erase using the current brush.';

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("eraser.h") ],
	code => <<'CODE'
success = eraser_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}

sub eraser {
    $blurb = 'Erase using the current brush.';

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	&stroke_arg,
	{ name => 'hardness', type => 'enum BrushApplicationMode (no PRESSURE)',
	  desc => '%%desc%%' },
	{ name => 'method', type => 'enum PaintApplicationMode',
	  desc => '%%desc%%' }
    );

    %invoke = (
	headers => [ qw("eraser.h") ],
	code => <<'CODE'
success = eraser_non_gui (drawable, num_strokes, strokes, hardness, method, TRUE);
CODE
    );
}

sub anti_eraser {
    $blurb = 'Anti-erase using the current brush.';

    $help = <<'HELP';
This tool anti-erases using the current brush mask. If the specified drawable
contains an alpha channel, then the erased pixels will become opaque.
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	&stroke_arg,
	{ name => 'hardness', type => 'enum BrushApplicationMode (no PRESSURE)',
	  desc => '%%desc%%' },
	{ name => 'method', type => 'enum PaintApplicationMode',
	  desc => '%%desc%%' }
    );

    %invoke = (
	headers => [ qw("eraser.h") ],
	code => <<'CODE'
success = eraser_non_gui (drawable, num_strokes, strokes, hardness, method, TRUE);
CODE
    );
}

sub flip {
    $blurb = <<'BLURB';
Flip the specified drawable about its center either vertically or
horizontally.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'flip_type', type => &std_orientation_enum,
	  desc => 'Type of flip: %%desc%%' }
    );

    @outargs = ( &drawable_out_arg('flipped') );

    %invoke = (
	headers => [ qw("flip_tool.h" "transform_core.h" "undo.h") ],
	vars => [ 'GimpImage *gimage', 'TileManager *float_tiles, *new_tiles',
		   'Layer *layer', 'gboolean new_layer' ],
	code => <<'CODE'
{
  gimage = 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 = transform_core_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)
    {
      layer = transform_core_paste (gimage, drawable, new_tiles, new_layer);
      success = (layer != NULL);
    }
  else
    success = FALSE;

  /* push the undo group end  */
  undo_push_group_end (gimage);
}
CODE
    );
}

sub free_select {
    $blurb = 'Create a polygonal selection over the specified image.';

    $help = <<'HELP';
This tool creates a polygonal selection over the specified image. The polygonal
region can be either added to, subtracted from, or replace the contents of the
previous selection mask. The polygon is specified through an array of floating
point numbers and its length. The length of array must be 2n, where n is the
number of points. Each point is defined by 2 floating point values which
correspond to the x and y coordinates. If the final point does not connect to
the starting point, a connecting segment is automatically added. If the feather
option is enabled, the resulting selection is blurred before combining. The
blur is a gaussian blur with the specified feather radius.
HELP

    &std_pdb_misc;

    @inargs = (
	&std_image_arg,
	{ name => 'segs', type => 'floatarray',
	  desc => 'Array of points: { p1.x, p1.y, p2.x, p2.y, ...,
	  	   pn.x, pn.y}',
	  array => { type => '2 <= int32', on_success => 'num_segs /= 2;',
		     desc => 'Number of points (count 1 coordinate as two
		     	      points)' } },
	&operation_arg,
	&std_antialias_arg,
	&feather_select_args
    );

    %invoke = (
	headers => [ qw("free_select.h") ],
	code => <<'CODE'
free_select (gimage, num_segs, (ScanConvertPoint *) segs, operation,
	     antialias, feather, feather_radius);
CODE
    );
}

sub fuzzy_select {
    $blurb = <<'BLURB';
Create a fuzzy selection starting at the specified coordinates on the specified
drawable.
BLURB

    $help = <<'HELP';
This tool creates a fuzzy selection over the specified image. A fuzzy selection
is determined by a seed fill under the constraints of the specified threshold.
Essentially, the color at the specified coordinates (in the drawable) is
measured and the selection expands outwards from that point to any adjacent
pixels which are not significantly different (as determined by the threshold
value). This process continues until no more expansion is possible. The
antialiasing parameter allows the final selection mask to contain intermediate
values based on close misses to the threshold bar at pixels along the seed fill
boundary. Feathering can be enabled optionally and is controlled with the
"feather_radius" paramter. 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. If the sample
is merged, the specified coordinates are relative to the image origin;
otherwise, they are relative to the drawable's origin.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'x', type => 'float',
	  desc => 'x coordinate of initial seed fill point: (image
	  	   coordinates)' },
	{ name => 'y', type => 'float',
	  desc => 'y coordinate of initial seed fill point: (image
	  	   coordinates)' },
	&threshold_arg,
	&operation_arg,
	&std_antialias_arg,
	&feather_select_args,
	&sample_merged_arg
    );

    %invoke = (
	headers => [ qw("fuzzy_select.h") ],
	vars => [ 'GimpImage *gimage', 'Channel *new, *old_fuzzy_mask' ],
	code => <<'CODE'
{
  gimage = drawable_gimage (GIMP_DRAWABLE (drawable));

  new = find_contiguous_region (gimage, drawable, antialias, threshold,
				x, y, sample_merged);
  old_fuzzy_mask = fuzzy_mask;
  fuzzy_mask = new;

  drawable = sample_merged ? NULL : drawable;
  fuzzy_select (gimage, drawable, operation, feather, feather_radius);

  fuzzy_mask = old_fuzzy_mask;
}
CODE
    );
}

sub paintbrush {
    $blurb = <<'BLURB';
Paint in the current brush with optional fade out parameter and pull colors
from a gradient.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'fade_out', type => '0 <= float',
	  desc => 'Fade out parameter: %%desc%%' },
	&stroke_arg,
	{ name => 'method', type => 'enum PaintApplicationMode',
	  desc => '%%desc%%' },
	{ name => 'gradient_length', type => '0 <= float',
	  desc => 'Length of gradient to draw: %%desc%%' }
    );

    %invoke = (
	headers => [ qw("paintbrush.h") ],
	code => <<'CODE'
success = paintbrush_non_gui (drawable, num_strokes, strokes, fade_out,
			      method, gradient_length);
CODE
    );
}

sub paintbrush_default {
    $blurb = <<'BLURB';
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.
BLURB

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("paintbrush.h") ],
	code => <<'CODE'
success = paintbrush_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}

sub pencil {
    $blurb = 'Paint in the current brush without sub-pixel sampling.';

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("pencil.h") ],
	code => 'success = pencil_non_gui (drawable, num_strokes, strokes);'
    );
}

sub perspective {
    $blurb = <<'BLURB';
Perform a possibly non-affine transformation on the specified drawable.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'interpolation', type => 'boolean',
	  desc => 'Whether to use interpolation' }
    );

    my $pos = 0;
    foreach $where (qw(upper-left upper-right lower-left lower-right)) {
	foreach (qw(x y)) {
	    push @inargs,
	    	 { name => "$_$pos", type => 'float',
		   desc => "The new $_ coordinate of $where corner of original
			    bounding box",
		   alias => "trans_info[\U$_\E$pos]", no_declare => 1 }
	}
	$pos++;
    }

    @outargs = ( &drawable_out_arg('newly mapped') );

    %invoke = (
	headers => [ qw("perspective_tool.h" "transform_core.h"
			"tile_manager_pvt.h") ],
	vars => [ 'GimpImage *gimage', 'TileManager *float_tiles, *new_tiles',
		  'Layer *layer', 'gboolean new_layer', 'double cx, cy',
		  'double scalex, scaley', 'double trans_info[8]',
		  'GimpMatrix3 m, matrix' ],
	code => <<'CODE'
{
  gimage = 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 = transform_core_cut (gimage, drawable, &new_layer);

  /* Determine the perspective transform that maps from
   * the unit cube to the trans_info coordinates
   */
  perspective_find_transform (trans_info, m);

  cx     = float_tiles->x;
  cy     = float_tiles->y;
  scalex = 1.0;
  scaley = 1.0;
  if (float_tiles->width)
    scalex = 1.0 / float_tiles->width;
  if (float_tiles->height)
    scaley = 1.0 / float_tiles->height;

  /* 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 = perspective_tool_perspective (gimage, drawable, NULL,
					    float_tiles, interpolation,
					    matrix);

  /* Free the cut/copied buffer */
  tile_manager_destroy (float_tiles);

  if (new_tiles)
    {
      layer = transform_core_paste (gimage, drawable, new_tiles, new_layer);
      success = layer != NULL;
    }
  else
    success = FALSE;

  /*  push the undo group end  */
  undo_push_group_end (gimage);
}
CODE
    );
}

sub rect_select {
    $blurb = 'Create a rectangular selection over the specified image;';

    $help = <<'HELP';
This tool creates a rectangular selection over the specified image. The
rectangular region can be either added to, subtracted from, or replace the
contents of the previous selection mask. If the feather option is enabled, the
resulting selection is blurred before combining. The blur is a gaussian blur
with the specified feather radius.
HELP

    &std_pdb_misc;

    @inargs = (
	&std_image_arg,
	{ name => 'x', type => 'float',
	  desc => 'x coordinate of upper-left corner of rectangle' },
	{ name => 'y', type => 'float',
	  desc => 'y coordinate of upper-left corner of rectangle' },
	{ name => 'width', type => '0 < float',
	  desc => 'The width of the rectangle: %%desc%%' },
	{ name => 'height', type => '0 < float',
	  desc => 'The height of the rectangle: %%desc%%' },
	&operation_arg,
	&feather_select_args,
    );

    %invoke = (
	headers => [ qw("rect_select.h") ],
	code => <<'CODE'
rect_select (gimage, (int) x, (int) y, (int) width, (int) height,
	     operation, feather, feather_radius);
CODE
    );
}

sub rotate {
    $blurb = <<'BLURB';
Rotate the specified drawable about its center through the specified angle.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'interpolation', type => 'boolean',
	  desc => 'Whether to use interpolation' },
	{ name => 'angle', type => 'float',
	  desc => 'The angle of rotation (radians)' }
    );

    @outargs = ( &drawable_out_arg('rotated') );

    %invoke = (
	headers => [ qw("rotate_tool.h" "transform_core.h") ],
	vars => [ 'GimpImage *gimage', 'TileManager *float_tiles, *new_tiles',
		  'Layer *layer', 'gboolean new_layer', 'double cx, cy',
		  'GimpMatrix3 matrix' ],
	code => <<'CODE'
{
  gimage = 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 = transform_core_cut (gimage, drawable, &new_layer);

  cx = float_tiles->x + float_tiles->width / 2.0;
  cy = float_tiles->y + float_tiles->height / 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 = rotate_tool_rotate (gimage, drawable, NULL, angle,
				  float_tiles, interpolation, matrix);

  /* Free the cut/copied buffer */
  tile_manager_destroy (float_tiles);

  if (new_tiles)
    {
      layer = transform_core_paste (gimage, drawable, new_tiles, new_layer);
      success = layer != NULL;
    }
  else
    success = FALSE;

  /* Push the undo group end */
  undo_push_group_end (gimage);
}
CODE
    );
}

sub scale {
    $blurb = 'Scale the specified drawable.';

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'interpolation', type => 'boolean',
	  desc => 'Whether to use interpolation' }
    );

    my $pos = 0;
    foreach $where (qw(upper-left lower-right)) {
	foreach (qw(x y)) {
	    push @inargs,
	    	 { name => "$_$pos", type => 'float',
		   desc => "The new $_ coordinate of $where corner of newly
			    scaled region",
		   alias => "trans_info[\U$_\E$pos]", no_declare => 1 }
	}
	$pos++;
    }

    @outargs = ( &drawable_out_arg('scaled') );

    %invoke = (
	headers => [ qw("scale_tool.h" "transform_core.h"
			"tile_manager_pvt.h") ],
	vars => [ 'GimpImage *gimage', 'TileManager *float_tiles, *new_tiles',
		  'Layer *layer', 'gboolean new_layer', 'double scalex, scaley',
		  'double trans_info[4]', 'GimpMatrix3 matrix' ],
	code => <<'CODE'
{
  if (trans_info[X0] < trans_info[X1] &&
      trans_info[Y0] < trans_info[X1])
    {
      gimage = 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 = transform_core_cut (gimage, drawable, &new_layer);

      scalex = scaley = 1.0;
      if (float_tiles->width)
        scalex = (trans_info[X1] - trans_info[X0]) /
		 (double) float_tiles->width;
      if (float_tiles->height)
        scaley = (trans_info[Y1] - trans_info[Y0]) /
		 (double) float_tiles->height;

      /* Assemble the transformation matrix */
      gimp_matrix3_identity  (matrix);
      gimp_matrix3_translate (matrix, float_tiles->x, float_tiles->y);
      gimp_matrix3_scale     (matrix, scalex, scaley);
      gimp_matrix3_translate (matrix, trans_info[X0], trans_info[Y0]);

      /* Scale the buffer */
      new_tiles = 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)
	{
          layer = transform_core_paste (gimage, drawable, new_tiles, new_layer);
	  success = layer != NULL;
	}
      else
        success = FALSE;

      /*  push the undo group end  */
      undo_push_group_end (gimage);
    }
  else
    success = FALSE;
}
CODE
    );
}

sub shear {
    $blurb = <<'BLURB';
Shear the specified drawable about its center by the specified magnitude.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	{ name => 'interpolation', type => 'boolean',
	  desc => 'Whether to use interpolation' },
	{ name => 'shear_type', type => &std_orientation_enum,
	  desc => 'Type of shear: %%desc%%' },
	{ name => 'magnitude', type => 'float',
	  desc => 'The magnitude of the shear' }
    );

    @outargs = ( &drawable_out_arg('sheared') );

    %invoke = (
	headers => [ qw("shear_tool.h" "transform_core.h"
			"tile_manager_pvt.h") ],
	vars => [ 'GimpImage *gimage', 'TileManager *float_tiles, *new_tiles',
		  'Layer *layer', 'gboolean new_layer', 'double cx, cy',
		  'GimpMatrix3 matrix' ],
	code => <<'CODE'
{
  gimage = 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 = transform_core_cut (gimage, drawable, &new_layer);

  cx = float_tiles->x + float_tiles->width  / 2.0;
  cy = float_tiles->y + float_tiles->height / 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 / float_tiles->height);
  else if (shear_type == ORIENTATION_VERTICAL)
    gimp_matrix3_yshear (matrix, magnitude / float_tiles->width);
  gimp_matrix3_translate (matrix, +cx, +cy);

  /* Shear the buffer */
  new_tiles = shear_tool_shear (gimage, drawable, NULL, float_tiles,
				interpolation, matrix);

  /* Free the cut/copied buffer */
  tile_manager_destroy (float_tiles);

  if (new_tiles)
    {
      layer = transform_core_paste (gimage, drawable, new_tiles, new_layer);
      success = layer != NULL;
    }
  else
    success = FALSE;

  /* Push the undo group end */
  undo_push_group_end (gimage);
}
CODE
    );
}


sub smudge {
    $blurb  = <<'BLURB';
Smudge image with varying pressure.
BLURB

    $help = <<'HELP';
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.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	 { name => 'pressure', type => '0 <= float <= 100',
	   desc => 'The pressure of the smudge strokes (%%desc%%)' },
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("smudge.h") ],
	code => <<'CODE'
success = smudge_non_gui (drawable, pressure, num_strokes, strokes);
CODE
    );
}

sub smudge_default {
    $blurb  = <<'BLURB';
Smudge image with varying pressure.
BLURB

    $help = <<'HELP';
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.
HELP

    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("smudge.h") ],
	code => <<'CODE'
success = smudge_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}

sub dodgeburn {
    $blurb  = <<'BLURB';
Dodgeburn image with varying exposure.
BLURB

    $help = <<'HELP';
Dodgebure. More details here later.
HELP


    $author = $copyright = 'Andy Thomas';
    $date = '1999';

    @inargs = (
	&drawable_arg,
	 { name => 'exposure', type => '0 <= float <= 100',
	   desc => 'The exposer of the  strokes (%%desc%%)' },
        { name => 'dodgeburn_type', type => 'enum DodgeBurnType',
	  desc => 'The type either dodge or burn: { %%desc%% }' },
        { name => 'dodgeburn_mode', type => 'enum DodgeBurnMode',
	  desc => 'The mode: { %%desc%% }' },
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("dodgeburn.h") ],
	code => <<'CODE'
success = dodgeburn_non_gui (drawable, exposure, dodgeburn_type, dodgeburn_mode, num_strokes, strokes);
CODE
    );
}

sub dodgeburn_default {
    $blurb  = <<'BLURB';
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.
BLURB

    $help = <<'HELP';
Dodgebure. More details here later.
HELP

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );

    %invoke = (
	headers => [ qw("dodgeburn.h") ],
	code => <<'CODE'
success = dodgeburn_non_gui_default (drawable, num_strokes, strokes);
CODE
    );
}


# Incomplete

sub ink {
    $blurb = 'Paint in the current brush without sub-pixel sampling.';

    $help = 'fixme fixme';

    &std_pdb_misc;

    @inargs = (
	&drawable_arg,
	&stroke_arg
    );
}

@procs = qw(airbrush airbrush_default blend bucket_fill by_color_select 
	    clone clone_default color_picker
	    convolve convolve_default crop dodgeburn dodgeburn_default
	    ellipse_select eraser eraser_default
	    flip free_select fuzzy_select
	    paintbrush paintbrush_default
	    pencil perspective rect_select rotate scale shear
	    smudge smudge_default);
%exports = (app => [@procs]);

$desc = 'Tool procedures';

1;
