PIKApp/pdb/groups/plug_in_compat.pdb

5605 lines
181 KiB
Plaintext

# PIKA - Photo and Image Kooker Application
# Copyright (C) 1995 Spencer Kimball and Peter Mattis
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# "Perlized" from C source by Manish Singh <yosh@gimp.org>
sub plug_in_alienmap2 {
$blurb = 'Alter colors in various psychedelic ways';
$help = <<'HELP';
No help yet. Just try it and you'll see!
HELP
&std_pdb_compat('gegl:alien-map');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'redfrequency', type => '0 <= float <= 20',
desc => 'Red/hue component frequency factor' },
{ name => 'redangle', type => '0 <= float <= 360',
desc => 'Red/hue component angle factor (0-360)' },
{ name => 'greenfrequency', type => '0 <= float <= 20',
desc => 'Green/saturation component frequency factor' },
{ name => 'greenangle', type => '0 <= float <= 360',
desc => 'Green/saturation component angle factor (0-360)' },
{ name => 'bluefrequency', type => '0 <= float <= 20',
desc => 'Blue/luminance component frequency factor' },
{ name => 'blueangle', type => '0 <= float <= 360',
desc => 'Blue/luminance component angle factor (0-360)' },
{ name => 'colormodel', type => '0 <= uchar <= 1',
desc => 'Color model { RGB-MODEL (0), HSL-MODEL (1) }' },
{ name => 'redmode', type => '0 <= uchar <= 1',
desc => 'Red/hue application mode { TRUE, FALSE }' },
{ name => 'greenmode', type => '0 <= uchar <= 1',
desc => 'Green/saturation application mode { TRUE, FALSE }' },
{ name => 'bluemode', type => '0 <= uchar <= 1',
desc => 'Blue/luminance application mode { TRUE, FALSE }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:alien-map",
"color-model", (gint) colormodel,
"cpn-1-frequency", (gdouble) redfrequency,
"cpn-2-frequency", (gdouble) greenfrequency,
"cpn-3-frequency", (gdouble) bluefrequency,
"cpn-1-phaseshift", (gdouble) redangle,
"cpn-2-phaseshift", (gdouble) greenangle,
"cpn-3-phaseshift", (gdouble) blueangle,
"cpn-1-keep", (gboolean) !redmode,
"cpn-2-keep", (gboolean) !greenmode,
"cpn-3-keep", (gboolean) !bluemode,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Alien Map"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_antialias {
$blurb = 'Antialias using the Scale3X edge-extrapolation algorithm';
$help = <<'HELP';
No more help.
HELP
&std_pdb_compat('gegl:antialias');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:antialias",
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Antialias"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_apply_canvas {
$blurb = 'Add a canvas texture to the image';
$help = <<'HELP';
This function applies a canvas texture map to the drawable.
HELP
&std_pdb_compat('gegl:texturize-canvas');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'direction', type => '0 <= int32 <= 3',
desc => 'Light direction (0 - 3)' },
{ name => 'depth', type => '1 <= int32 <= 50',
desc => 'Texture depth (1 - 50)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:texturize-canvas",
"direction", direction,
"depth", depth,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Apply Canvas"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_applylens {
$blurb = 'Simulate an elliptical lens over the image';
$help = <<'HELP';
This plug-in uses Snell's law to draw an ellipsoid lens over the image.
HELP
&std_pdb_compat('gegl:apply-lens');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'refraction', type => '1.0 <= float <= 100.0',
desc => 'Lens refraction index' },
{ name => 'keep_surroundings', type => 'boolean',
desc => 'Keep lens surroundings' },
{ name => 'set_background', type => 'boolean',
desc => 'Set lens surroundings to BG value' },
{ name => 'set_transparent', type => 'boolean', dead => 1,
desc => 'Set lens surroundings transparent' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
PikaRGB color;
GeglColor *gegl_color;
GeglNode *node;
if (set_background)
pika_context_get_background (context, &color);
else
pika_rgba_set (&color, 0.0, 0.0, 0.0, 0.0);
gegl_color = pika_gegl_color_new (&color, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:apply-lens",
"refraction-index", refraction,
"keep-surroundings", keep_surroundings,
"background-color", gegl_color,
NULL);
g_object_unref (gegl_color);
node = wrap_in_selection_bounds (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Apply Lens"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_autocrop {
$blurb = 'Remove empty borders from the image';
$help = <<'HELP';
Remove empty borders from the image.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image',
desc => 'Input image)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error))
{
gint x, y, width, height;
gint off_x, off_y;
pika_pickable_auto_shrink (PIKA_PICKABLE (drawable),
0, 0,
pika_item_get_width (PIKA_ITEM (drawable)),
pika_item_get_height (PIKA_ITEM (drawable)),
&x, &y, &width, &height);
pika_item_get_offset (PIKA_ITEM (drawable), &off_x, &off_y);
x += off_x;
y += off_y;
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_ITEM_RESIZE,
_("Autocrop image"));
if (x < 0 ||
y < 0 ||
x + width > pika_image_get_width (image) ||
y + height > pika_image_get_height (image))
{
/*
* partially outside the image area, we need to
* resize the image to be able to crop properly.
*/
pika_image_resize (image, context, width, height, -x, -y, NULL);
x = y = 0;
}
pika_image_crop (image, context, PIKA_FILL_TRANSPARENT,
x, y, width, height, TRUE);
pika_image_undo_group_end (image);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_autocrop_layer {
$blurb = 'Crop the selected layers based on empty borders of the input drawable';
$help = <<'HELP';
Crop the selected layers of the input "image" based on empty borders of the input "drawable".
\n\nThe input drawable serves as a base for detecting cropping extents (transparency or background color), and is not necessarily among the cropped layers (the current selected layers).
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image',
desc => 'Input image)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error))
{
GList *layers = pika_image_get_selected_layers (image);
GList *iter;
gint x, y, width, height;
if (layers)
{
switch (pika_pickable_auto_shrink (PIKA_PICKABLE (drawable),
0, 0,
pika_item_get_width (PIKA_ITEM (drawable)),
pika_item_get_height (PIKA_ITEM (drawable)),
&x, &y, &width, &height))
{
case PIKA_AUTO_SHRINK_SHRINK:
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_ITEM_RESIZE,
_("Autocrop layer"));
for (iter = layers; iter; iter = iter->next)
pika_item_resize (PIKA_ITEM (iter->data),
context, PIKA_FILL_TRANSPARENT,
width, height, -x, -y);
pika_image_undo_group_end (image);
break;
default:
break;
}
}
else
{
success = FALSE;
}
}
else
{
success = FALSE;
}
}
CODE
);
}
sub plug_in_autostretch_hsv {
$blurb = 'Stretch contrast to cover the maximum possible range';
$help = <<'HELP';
This simple plug-in does an automatic contrast stretch. For each
channel in the image, it finds the minimum and maximum values... it
uses those values to stretch the individual histograms to the full
contrast range. For some images it may do just what you want; for
others it may be total crap :). This version differs from Contrast
Autostretch in that it works in HSV space, and preserves hue.
HELP
&std_pdb_compat('gegl:stretch-contrast-hsv');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:stretch-contrast-hsv",
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Stretch Contrast HSV"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_bump_map {
$blurb = 'Create an embossing effect using a bump map';
$help = <<'HELP';
This plug-in uses the algorithm described by John Schlag,
"Fast Embossing Effects on Raster Image Data" in
Graphics GEMS IV (ISBN 0-12-336155-9).
It takes a drawable to be applied as a bump
map to another image and produces a nice embossing effect.
HELP
&std_pdb_compat('gegl:bump-map');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'bumpmap', type => 'drawable',
desc => 'Bump map drawable' },
{ name => 'azimuth', type => '0.0 <= float <= 360.0',
desc => 'Azimuth' },
{ name => 'elevation', type => '0.5 <= float <= 90.0',
desc => 'Elevation' },
{ name => 'depth', type => '1 <= int32 <= 65',
desc => 'Depth' },
{ name => 'xofs', type => 'int32',
desc => 'X offset' },
{ name => 'yofs', type => 'int32',
desc => 'Y offset' },
{ name => 'waterlevel', type => '0.0 <= float <= 1.0',
desc => 'Level that full transparency should represent' },
{ name => 'ambient', type => '0.0 <= float <= 1.0',
desc => 'Ambient lighting factor' },
{ name => 'compensate', type => 'boolean',
desc => 'Compensate for darkening' },
{ name => 'invert', type => 'boolean',
desc => 'Invert bumpmap' },
{ name => 'type', type => '0 <= int32 <= 3',
desc => 'Type of map { LINEAR (0), SPHERICAL (1), SINUSOIDAL (2) }' }
);
%invoke = (
code => <<'CODE'
{
success = bump_map (drawable,
bumpmap,
azimuth,
elevation,
depth,
xofs,
yofs,
waterlevel,
ambient,
compensate,
invert,
type,
FALSE,
progress,
error);
}
CODE
);
}
sub plug_in_bump_map_tiled {
$blurb = 'Create an embossing effect using a tiled image as a bump map';
$help = <<'HELP';
This plug-in uses the algorithm described by John Schlag,
"Fast Embossing Effects on Raster Image Data" in
Graphics GEMS IV (ISBN 0-12-336155-9).
It takes a drawable to be tiled and applied as a bump map
to another image and produces a nice embossing effect.
HELP
&std_pdb_compat('gegl:bump-map');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'bumpmap', type => 'drawable',
desc => 'Bump map drawable' },
{ name => 'azimuth', type => '0.0 <= float <= 360.0',
desc => 'Azimuth' },
{ name => 'elevation', type => '0.5 <= float <= 90.0',
desc => 'Elevation' },
{ name => 'depth', type => '1 <= int32 <= 65',
desc => 'Depth' },
{ name => 'xofs', type => 'int32',
desc => 'X offset' },
{ name => 'yofs', type => 'int32',
desc => 'Y offset' },
{ name => 'waterlevel', type => '0.0 <= float <= 1.0',
desc => 'Level that full transparency should represent' },
{ name => 'ambient', type => '0.0 <= float <= 1.0',
desc => 'Ambient lighting factor' },
{ name => 'compensate', type => 'boolean',
desc => 'Compensate for darkening' },
{ name => 'invert', type => 'boolean',
desc => 'Invert bumpmap' },
{ name => 'type', type => '0 <= int32 <= 3',
desc => 'Type of map { LINEAR (0), SPHERICAL (1), SINUSOIDAL (2) }' }
);
%invoke = (
code => <<'CODE'
{
success = bump_map (drawable,
bumpmap,
azimuth,
elevation,
depth,
xofs,
yofs,
waterlevel,
ambient,
compensate,
invert,
type,
TRUE,
progress,
error);
}
CODE
);
}
sub plug_in_c_astretch {
$blurb = 'Stretch contrast to cover the maximum possible range';
$help = <<'HELP';
This simple plug-in does an automatic contrast stretch. For each
channel in the image, it finds the minimum and maximum values... it
uses those values to stretch the individual histograms to the full
contrast range. For some images it may do just what you want; for
others it may not work that well.
HELP
&std_pdb_compat('gegl:stretch-contrast');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:stretch-contrast",
"keep-colors", (gboolean) FALSE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Stretch Contrast"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_cartoon {
$blurb = 'Simulate a cartoon by enhancing edges';
$help = <<'HELP';
Propagates dark values in an image based on each pixel's relative
darkness to a neighboring average. The idea behind this filter is to
give the look of a black felt pen drawing subsequently shaded with
color. This is achieved by darkening areas of the image which are
measured to be darker than a neighborhood average. In this way,
sufficiently large shifts in intensity are darkened to black. The rate
at which they are darkened to black is determined by the second
pct_black parameter. The mask_radius parameter controls the size of
the pixel neighborhood over which the average intensity is computed
and then compared to each pixel in the neighborhood to decide whether
or not to darken it to black. Large values for mask_radius result in
very thick black areas bordering the shaded regions of color and much
less detail for black areas everywhere including inside regions of
color. Small values result in more subtle pen strokes and detail
everywhere. Small values for the pct_black make the blend from the
color regions to the black border lines smoother and the lines
themselves thinner and less noticeable; larger values achieve the
opposite effect.
HELP
&std_pdb_compat('gegl:cartoon');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'mask_radius', type => '1.0 <= float <= 50.0',
desc => 'Cartoon mask radius (radius of pixel neighborhood)' },
{ name => 'pct_black', type => '0.0 <= float <= 1.0',
desc => 'Percentage of darkened pixels to set to black' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:cartoon",
"mask-radius", mask_radius,
"pct-black", pct_black,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Cartoon"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_colors_channel_mixer {
$blurb = 'Alter colors by mixing RGB Channels';
$help = <<'HELP';
This plug-in mixes the RGB channels.
HELP
&std_pdb_compat('gegl:channel-mixer');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'monochrome', type => '0 <= int32 <= 1',
desc => 'Monochrome { TRUE, FALSE }' },
{ name => 'rr_gain', type => '-2 <= float <= 2',
desc => 'Set the red gain for the red channel' },
{ name => 'rg_gain', type => '-2 <= float <= 2',
desc => 'Set the green gain for the red channel' },
{ name => 'rb_gain', type => '-2 <= float <= 2',
desc => 'Set the blue gain for the red channel' },
{ name => 'gr_gain', type => '-2 <= float <= 2',
desc => 'Set the red gain for the green channel' },
{ name => 'gg_gain', type => '-2 <= float <= 2',
desc => 'Set the green gain for the green channel' },
{ name => 'gb_gain', type => '-2 <= float <= 2',
desc => 'Set the blue gain for the green channel' },
{ name => 'br_gain', type => '-2 <= float <= 2',
desc => 'Set the red gain for the blue channel' },
{ name => 'bg_gain', type => '-2 <= float <= 2',
desc => 'Set the green gain for the blue channel' },
{ name => 'bb_gain', type => '-2 <= float <= 2',
desc => 'Set the blue gain for the blue channel' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node = NULL;
if (monochrome)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:mono-mixer",
"red", rr_gain,
"green", rg_gain,
"blue", rb_gain,
NULL);
}
else
{
node = gegl_node_new_child (NULL,
"operation", "gegl:channel-mixer",
"rr-gain", rr_gain,
"rg-gain", rg_gain,
"rb-gain", rb_gain,
"gr-gain", gr_gain,
"gg-gain", gg_gain,
"gb-gain", gb_gain,
"br-gain", br_gain,
"bg-gain", bg_gain,
"bb-gain", bb_gain,
NULL);
}
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Channel Mixer"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_colortoalpha {
$blurb = 'Convert a specified color to transparency';
$help = <<'HELP';
This replaces as much of a given color as possible in each pixel with
a corresponding amount of alpha, then readjusts the color accordingly.
HELP
&std_pdb_misc;
$date = '1999';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'color', type => 'color',
desc => 'Color to remove' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglColor *gegl_color = pika_gegl_color_new (&color, NULL);
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:color-to-alpha",
"color", gegl_color,
NULL);
g_object_unref (gegl_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Color to Alpha"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_convmatrix {
$blurb = 'Apply a generic 5x5 convolution matrix';
$help = <<'HELP';
Apply a generic 5x5 convolution matrix.
HELP
&std_pdb_compat('gegl:convolution-matrix');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'matrix', type => 'floatarray',
desc => 'The 5x5 convolution matrix',
array => { name => 'argc_matrix',
desc => 'The number of elements in the following array, must always be 25' } },
{ name => 'alpha_alg', type => 'boolean',
desc => 'Enable weighting by alpha channel' },
{ name => 'divisor', type => 'float',
desc => 'Divisor' },
{ name => 'offset', type => 'float',
desc => 'Offset' },
{ name => 'channels', type => 'int32array',
desc => 'Mask of the channels to be filtered',
array => { name => 'argc_channels',
desc => 'The number of elements in following array, must always be 5' } },
{ name => 'bmode', type => '0 <= int32 <= 2',
desc => 'Mode for treating image borders { EXTEND (0), WRAP (1), CLEAR (2) }' }
);
%invoke = (
code => <<'CODE'
{
if (argc_matrix != 25)
{
g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT,
_("Array 'matrix' has only %d members, must have 25"),
argc_matrix);
success = FALSE;
}
if (success && argc_channels != 5)
{
g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT,
_("Array 'channels' has only %d members, must have 5"),
argc_channels);
success = FALSE;
}
if (success &&
pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglAbyssPolicy border = GEGL_ABYSS_CLAMP;
gboolean r = channels[1];
gboolean g = channels[2];
gboolean b = channels[3];
gboolean a = channels[4];
if (pika_drawable_is_gray (drawable))
{
r = channels[0];
g = channels[0];
b = channels[0];
}
switch (bmode)
{
case 0: border = GEGL_ABYSS_CLAMP; break;
case 1: border = GEGL_ABYSS_LOOP; break;
case 2: border = GEGL_ABYSS_NONE; break;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:convolution-matrix",
"a1", matrix[0],
"a2", matrix[1],
"a3", matrix[2],
"a4", matrix[3],
"a5", matrix[4],
"b1", matrix[5],
"b2", matrix[6],
"b3", matrix[7],
"b4", matrix[8],
"b5", matrix[9],
"c1", matrix[10],
"c2", matrix[11],
"c3", matrix[12],
"c4", matrix[13],
"c5", matrix[14],
"d1", matrix[15],
"d2", matrix[16],
"d3", matrix[17],
"d4", matrix[18],
"d5", matrix[19],
"e1", matrix[20],
"e2", matrix[21],
"e3", matrix[22],
"e4", matrix[23],
"e5", matrix[24],
"divisor", divisor,
"offset", offset,
"red", r,
"green", g,
"blue", b,
"alpha", a,
"normalize", FALSE,
"alpha-weight", alpha_alg,
"border", border,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Convolution Matrix"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_cubism {
$blurb = 'Convert the image into randomly rotated square blobs';
$help = <<'HELP';
Convert the image into randomly rotated square blobs.
HELP
&std_pdb_compat('gegl:cubism');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'tile_size', type => '0.0 <= float <= 100.0',
desc => 'Average diameter of each tile (in pixels)' },
{ name => 'tile_saturation', type => '0.0 <= float <= 10.0',
desc => 'Expand tiles by this amount' },
{ name => 'bg_color', type => '0 <= int32 <= 1',
desc => 'Background color { BLACK (0), BG (1) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
PikaRGB color;
GeglColor *gegl_color;
GeglNode *node;
if (bg_color)
{
pika_context_get_background (context, &color);
pika_rgb_set_alpha (&color, 0.0);
}
else
{
pika_rgba_set (&color, 0.0, 0.0, 0.0, 0.0);
}
gegl_color = pika_gegl_color_new (&color, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:cubism",
"tile-size", tile_size,
"tile-saturation", tile_saturation,
"bg-color", gegl_color,
NULL);
g_object_unref (gegl_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Cubism"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_deinterlace {
$blurb = 'Fix images where every other row is missing';
$help = <<'HELP';
Deinterlace is useful for processing images from video capture
cards. When only the odd or even fields get captured, deinterlace can
be used to interpolate between the existing fields to correct this.
HELP
&std_pdb_compat('gegl:deinterlace');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'evenodd', type => '0 <= int32 <= 1',
desc => 'Which lines to keep { KEEP-ODD (0), KEEP-EVEN (1)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:deinterlace",
"keep", evenodd ? 0 : 1,
"orientation", 0, /* HORIZONTAL */
"size", 1,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Deinterlace"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_diffraction {
$blurb = 'Generate diffraction patterns';
$help = <<'HELP';
Help? What help?
HELP
&std_pdb_compat('gegl:diffraction-patterns');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'lam_r', type => '0.0 <= float <= 20.0',
desc => 'Light frequency (red)' },
{ name => 'lam_g', type => '0.0 <= float <= 20.0',
desc => 'Light frequency (green)' },
{ name => 'lam_b', type => '0.0 <= float <= 20.0',
desc => 'Light frequency (blue)' },
{ name => 'contour_r', type => '0.0 <= float <= 10.0',
desc => 'Number of contours (red)' },
{ name => 'contour_g', type => '0.0 <= float <= 10.0',
desc => 'Number of contours (green)' },
{ name => 'contour_b', type => '0.0 <= float <= 10.0',
desc => 'Number of contours (blue)' },
{ name => 'edges_r', type => '0.0 <= float <= 1.0',
desc => 'Number of sharp edges (red)' },
{ name => 'edges_g', type => '0.0 <= float <= 1.0',
desc => 'Number of sharp edges (green)' },
{ name => 'edges_b', type => '0.0 <= float <= 1.0',
desc => 'Number of sharp edges (blue)' },
{ name => 'brightness', type => '0.0 <= float <= 1.0',
desc => 'Brightness and shifting/fattening of contours' },
{ name => 'scattering', type => '0.0 <= float <= 100.0',
desc => 'Scattering (Speed vs. quality)' },
{ name => 'polarization', type => '-1.0 <= float <= 1.0',
desc => 'Polarization' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gint x, y, width, height;
pika_item_mask_intersect (PIKA_ITEM (drawable), &x, &y, &width, &height);
node = gegl_node_new_child (NULL,
"operation", "gegl:diffraction-patterns",
"red-frequency", lam_r,
"green-frequency", lam_g,
"blue-frequency", lam_b,
"red-contours", contour_r,
"green-contours", contour_g,
"blue-contours", contour_b,
"red-sedges", edges_r,
"green-sedges", edges_g,
"blue-sedges", edges_b,
"brightness", brightness,
"scattering", scattering,
"polarization", polarization,
"width", width,
"height", height,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Diffraction Patterns"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_displace {
$blurb = 'Displace pixels as indicated by displacement maps';
$help = <<'HELP';
Displaces the contents of the specified drawable by the amounts specified
by 'amount-x' and 'amount-y' multiplied by the luminance of corresponding
pixels in the 'displace-map' drawables.
HELP
&std_pdb_compat('gegl:displace');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'amount_x', type => '-500.0 <= float <= 500.0',
desc => 'Displace multiplier for x direction' },
{ name => 'amount_y', type => '-500.0 <= float <= 500.0',
desc => 'Displace multiplier for y direction' },
{ name => 'do_x', type => 'boolean',
desc => 'Displace in x direction ?' },
{ name => 'do_y', type => 'boolean',
desc => 'Displace in y direction ?' },
{ name => 'displace_map_x', type => 'drawable',
desc => 'Displacement map for x direction' },
{ name => 'displace_map_y', type => 'drawable',
desc => 'Displacement map for y direction' },
{ name => 'displace_type', type => '1 <= int32 <= 3',
desc => 'Edge behavior { WRAP (1), SMEAR (2), BLACK (3) }' }
);
%invoke = (
code => <<'CODE'
{
success = displace (drawable,
amount_x,
amount_y,
do_x,
do_y,
displace_map_x,
displace_map_y,
displace_type,
0,
progress,
error);
}
CODE
);
}
sub plug_in_displace_polar {
$blurb = 'Displace pixels as indicated by displacement maps';
$help = <<'HELP';
Just like plug-in-displace but working in polar coordinates.
The drawable is whirled and pinched according to the map.
HELP
&std_pdb_compat('gegl:displace');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'amount_x', type => '-500.0 <= float <= 500.0',
desc => 'Displace multiplier for radial direction' },
{ name => 'amount_y', type => '-500.0 <= float <= 500.0',
desc => 'Displace multiplier for tangent direction' },
{ name => 'do_x', type => 'boolean',
desc => 'Displace in radial direction ?' },
{ name => 'do_y', type => 'boolean',
desc => 'Displace in tangent direction ?' },
{ name => 'displace_map_x', type => 'drawable',
desc => 'Displacement map for radial direction' },
{ name => 'displace_map_y', type => 'drawable',
desc => 'Displacement map for tangent direction' },
{ name => 'displace_type', type => '1 <= int32 <= 3',
desc => 'Edge behavior { WRAP (1), SMEAR (2), BLACK (3) }' }
);
%invoke = (
code => <<'CODE'
{
success = displace (drawable,
amount_x,
amount_y,
do_x,
do_y,
displace_map_x,
displace_map_y,
displace_type,
1,
progress,
error);
}
CODE
);
}
sub plug_in_dog {
$blurb = 'Edge detection with control of edge thickness';
$help = <<'HELP';
Applies two Gaussian blurs to the drawable, and subtracts the results.
This is robust and widely used method for detecting edges.
HELP
&std_pdb_compat('gegl:difference-of-gaussians');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image',
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'inner', type => '0.0 <= float <= 10.0',
desc => 'Radius of inner gaussian blur in pixels' },
{ name => 'outer', type => '0.0 <= float <= 10.0',
desc => 'Radius of outer gaussian blur in pixels' },
{ name => 'normalize', type => 'boolean',
desc => 'Normalize' },
{ name => 'invert', type => 'boolean',
desc => 'Invert' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
if (normalize || invert)
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_MISC,
C_("undo-type", "DoG Edge Detect"));
node = gegl_node_new_child (NULL,
"operation", "gegl:difference-of-gaussians",
"radius1", inner * 0.32,
"radius2", outer * 0.32,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "DoG Edge Detect"),
node);
g_object_unref (node);
if (normalize)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:stretch-contrast",
"keep-colors", TRUE,
"perceptual", TRUE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Normalize"),
node);
g_object_unref (node);
}
if (invert)
pika_drawable_apply_operation_by_name (drawable, progress,
C_("undo-type", "Invert"),
"gegl:invert-gamma",
NULL);
if (normalize || invert)
pika_image_undo_group_end (image);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_edge {
$blurb = 'Several simple methods for detecting edges';
$help = <<'HELP';
Perform edge detection on the contents of the specified drawable.
AMOUNT is an arbitrary constant, WRAPMODE is like displace plug-in
(useful for tileable image). EDGEMODE sets the kind of matrix transform
applied to the pixels, SOBEL was the method used in older versions.
HELP
&std_pdb_compat('gegl:edge');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'amount', type => '1.0 <= float <= 10.0',
desc => 'Edge detection amount' },
{ name => 'warpmode', type => '0 <= int32 <= 3',
desc => 'Edge detection behavior { NONE (0), WRAP (1), SMEAR (2), BLACK (3) }' },
{ name => 'edgemode', type => '0 <= int32 <= 5',
desc => 'Edge detection algorithm { SOBEL (0), PREWITT (1), GRADIENT (2), ROBERTS (3), DIFFERENTIAL (4), LAPLACE (5) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglAbyssPolicy border_behavior = GEGL_ABYSS_NONE;
switch (warpmode)
{
case 0:
border_behavior = GEGL_ABYSS_NONE;
break;
case 1:
border_behavior = GEGL_ABYSS_LOOP;
break;
case 2:
border_behavior = GEGL_ABYSS_CLAMP;
break;
case 3:
border_behavior = GEGL_ABYSS_BLACK;
break;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:edge",
"algorithm", edgemode,
"amount", amount,
"border-behavior", border_behavior,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Edge"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_emboss {
$blurb = 'Simulate an image created by embossing';
$help = <<'HELP';
Emboss or Bumpmap the given drawable, specifying the angle and
elevation for the light source.
HELP
&std_pdb_compat('gegl:emboss');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'azimuth', type => '0.0 <= float <= 360.0',
desc => 'The Light Angle (degrees)' },
{ name => 'elevation', type => '0.0 <= float <= 180',
desc => 'The Elevation Angle (degrees)' },
{ name => 'depth', type => '0 < int32 < 100',
default => 1, desc => 'The Filter Width' },
{ name => 'emboss', type => 'boolean',
desc => 'Emboss (TRUE), Bumpmap (FALSE)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:emboss",
"type", emboss ? 0 : 1,
"azimuth", azimuth,
"elevation", elevation,
"depth", depth,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Emboss"),
node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_engrave {
$blurb = 'Simulate an antique engraving';
$help = <<'HELP';
Creates a black-and-white 'engraved' version of an image as seen in
old illustrations.
HELP
&std_pdb_compat('gegl:engrave');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'height', type => '2 <= int32 <= 16',
desc => 'Resolution in pixels' },
{ name => 'limit', type => 'boolean',
desc => 'Limit line width' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:engrave",
"row-height", height,
"limit", limit,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Engrave"),
node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_exchange {
$blurb = 'Swap one color with another';
$help = <<'HELP';
Exchange one color with another, optionally setting a threshold to
convert from one shade to another.
HELP
&std_pdb_compat('gegl:color-exchange');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'from_red', type => 'uchar',
desc => 'Red value (from)' },
{ name => 'from_green', type => 'uchar',
desc => 'Green value (from)' },
{ name => 'from_blue', type => 'uchar',
desc => 'Blue value (from)' },
{ name => 'to_red', type => 'uchar',
desc => 'Red value (to)' },
{ name => 'to_green', type => 'uchar',
desc => 'Green value (to)' },
{ name => 'to_blue', type => 'uchar',
desc => 'Blue value (to)' },
{ name => 'red_threshold', type => 'uchar',
desc => 'Red threshold' },
{ name => 'green_threshold', type => 'uchar',
desc => 'Green threshold' },
{ name => 'blue_threshold', type => 'uchar',
desc => 'Blue threshold' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
PikaRGB from;
PikaRGB to;
GeglColor *gegl_from;
GeglColor *gegl_to;
GeglNode *node;
pika_rgb_set_uchar (&from, from_red, from_green, from_blue);
pika_rgb_set_uchar (&to, to_red, to_green, to_blue);
gegl_from = pika_gegl_color_new (&from, NULL);
gegl_to = pika_gegl_color_new (&to, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:color-exchange",
"from-color", gegl_from,
"to-color", gegl_to,
"red-threshold", red_threshold / 255.0,
"green-threshold", green_threshold / 255.0,
"blue-threshold", blue_threshold / 255.0,
NULL);
g_object_unref (gegl_from);
g_object_unref (gegl_to);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Color Exchange"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_flarefx {
$blurb = 'Add a lens flare effect';
$help = <<'HELP';
Adds a lens flare effects. Makes your image look like it was snapped
with a cheap camera with a lot of lens :)
HELP
&std_pdb_compat('gegl:lens-flare');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'pos_x', type => 'int32',
desc => 'X-Position' },
{ name => 'pos_y', type => 'int32',
desc => 'Y-Position' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gint width = pika_item_get_width (PIKA_ITEM (drawable));
gint height = pika_item_get_height (PIKA_ITEM (drawable));
gdouble x = (gdouble) pos_x / (gdouble) width;
gdouble y = (gdouble) pos_y / (gdouble) height;
node = gegl_node_new_child (NULL,
"operation", "gegl:lens-flare",
"pos-x", x,
"pos-y", y,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Lens Flare"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_fractal_trace {
$blurb = 'Transform image with the Mandelbrot Fractal';
$help = <<'HELP';
Transform image with the Mandelbrot Fractal
HELP
&std_pdb_compat('gegl:fractal-trace');
$date = '2018';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'xmin', type => '-50.0 <= float <= 50.0',
desc => 'xmin fractal image delimiter' },
{ name => 'xmax', type => '-50.0 <= float <= 50.0',
desc => 'xmax fractal image delimiter' },
{ name => 'ymin', type => '-50.0 <= float <= 50.0',
desc => 'ymin fractal image delimiter' },
{ name => 'ymax', type => '-50.0 <= float <= 50.0',
desc => 'ymax fractal image delimiter' },
{ name => 'depth', type => '1 <= int32 <= 65536',
desc => 'Trace depth' },
{ name => 'outside_type', type => '0 <= int32 <= 3',
desc => 'Outside type { WRAP (0), TRANS (1), BLACK (2), WHITE (3) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglAbyssPolicy abyss = GEGL_ABYSS_LOOP;
switch (outside_type)
{
case 0: abyss = GEGL_ABYSS_LOOP; break;
case 1: abyss = GEGL_ABYSS_NONE; break;
case 2: abyss = GEGL_ABYSS_BLACK; break;
case 3: abyss = GEGL_ABYSS_WHITE; break;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:fractal-trace",
"X1", xmin,
"X2", xmax,
"Y1", ymin,
"Y2", ymax,
"depth", depth,
"abyss-policy", abyss,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Fractal Trace"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_gauss {
$blurb = 'Simplest, most commonly used way of blurring';
$help = <<'HELP';
Applies a gaussian blur to the drawable, with specified radius of affect.
The standard deviation of the normal distribution used to modify pixel
values is calculated based on the supplied radius.
Horizontal and vertical blurring can be independently invoked by specifying
only one to run. The 'method' parameter is ignored.
HELP
&std_pdb_compat('gegl:gaussian-blur');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'horizontal', type => '0.0 <= float <= 500.0',
desc => 'Horizontal radius of gaussian blur (in pixels' },
{ name => 'vertical', type => '0.0 <= float <= 500.0',
desc => 'Vertical radius of gaussian blur (in pixels' },
{ name => 'method', type => '0 <= int32 <= 1', dead => 1,
desc => 'Blur method { IIR (0), RLE (1) }' }
);
%invoke = (
code => <<'CODE'
{
success = gaussian_blur (drawable, horizontal, vertical, progress, error);
}
CODE
);
}
sub plug_in_gauss_iir {
$blurb = 'Apply a gaussian blur';
$help = <<'HELP';
Applies a gaussian blur to the drawable, with specified radius of affect.
The standard deviation of the normal distribution used to modify pixel
values is calculated based on the supplied radius. Horizontal and vertical
blurring can be independently invoked by specifying only one to run.
HELP
&std_pdb_compat('gegl:gaussian-blur');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '0.0 <= float <= 500.0',
desc => 'Radius of gaussian blur (in pixels' },
{ name => 'horizontal', type => 'boolean',
desc => 'Blur in horizontal direction' },
{ name => 'vertical', type => 'boolean',
desc => 'Blur in vertical direction' }
);
%invoke = (
code => <<'CODE'
{
success = gaussian_blur (drawable,
horizontal ? radius : 0.0,
vertical ? radius : 0.0,
progress, error);
}
CODE
);
}
sub plug_in_gauss_iir2 {
$blurb = 'Apply a gaussian blur';
$help = <<'HELP';
Applies a gaussian blur to the drawable, with specified radius of affect.
The standard deviation of the normal distribution used to modify pixel
values is calculated based on the supplied radius. Horizontal and vertical
blurring can be independently invoked by specifying only one to run.
HELP
&std_pdb_compat('gegl:gaussian-blur');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'horizontal', type => '0.0 <= float <= 500.0',
desc => 'Horizontal radius of gaussian blur (in pixels' },
{ name => 'vertical', type => '0.0 <= float <= 500.0',
desc => 'Vertical radius of gaussian blur (in pixels' },
);
%invoke = (
code => <<'CODE'
{
success = gaussian_blur (drawable, horizontal, vertical, progress, error);
}
CODE
);
}
sub plug_in_gauss_rle {
$blurb = 'Apply a gaussian blur';
$help = <<'HELP';
Applies a gaussian blur to the drawable, with specified radius of affect.
The standard deviation of the normal distribution used to modify pixel
values is calculated based on the supplied radius. Horizontal and vertical
blurring can be independently invoked by specifying only one to run.
HELP
&std_pdb_compat('gegl:gaussian-blur');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '0.0 <= float <= 500.0',
desc => 'Radius of gaussian blur (in pixels' },
{ name => 'horizontal', type => 'boolean',
desc => 'Blur in horizontal direction' },
{ name => 'vertical', type => 'boolean',
desc => 'Blur in vertical direction' }
);
%invoke = (
code => <<'CODE'
{
success = gaussian_blur (drawable,
horizontal ? radius : 0.0,
vertical ? radius : 0.0,
progress, error);
}
CODE
);
}
sub plug_in_gauss_rle2 {
$blurb = 'Apply a gaussian blur';
$help = <<'HELP';
Applies a gaussian blur to the drawable, with specified radius of affect.
The standard deviation of the normal distribution used to modify pixel
values is calculated based on the supplied radius. Horizontal and vertical
blurring can be independently invoked by specifying only one to run.
HELP
&std_pdb_compat('gegl:gaussian-blur');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'horizontal', type => '0.0 <= float <= 500.0',
desc => 'Horizontal radius of gaussian blur (in pixels' },
{ name => 'vertical', type => '0.0 <= float <= 500.0',
desc => 'Vertical radius of gaussian blur (in pixels' },
);
%invoke = (
code => <<'CODE'
{
success = gaussian_blur (drawable, horizontal, vertical, progress, error);
}
CODE
);
}
sub plug_in_glasstile {
$blurb = 'Simulate distortion caused by square glass tiles';
$help = <<'HELP';
Divide the image into square glassblocks in which the image is
refracted.
HELP
&std_pdb_compat('gegl:tile-glass');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'tilex', type => '10 <= int32 <= 500',
desc => 'Tile width' },
{ name => 'tiley', type => '10 <= int32 <= 500',
desc => 'Tile height' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:tile-glass",
"tile-width", tilex,
"tile-height", tiley,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Glass Tile"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_hsv_noise {
$blurb = 'Randomize hue, saturation and value independently';
$help = <<'HELP';
Scattering pixel values in HSV space
HELP
&std_pdb_compat('gegl:noise-hsv');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'holdness', type => '1 <= int32 <= 8',
desc => 'Convolution strength' },
{ name => 'hue_distance', type => '0 <= int32 <= 180',
desc => 'Scattering of hue angle' },
{ name => 'saturation_distance', type => '0 <= int32 <= 255',
desc => 'Distribution distance on saturation axis' },
{ name => 'value_distance', type => '0 <= int32 <= 255',
desc => 'Distribution distance on value axis' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gdouble saturation = saturation_distance / 255.0;
gdouble value = value_distance / 255.0;
node = gegl_node_new_child (NULL,
"operation", "gegl:noise-hsv",
"holdness", (gint) holdness,
"hue-distance", (gdouble) hue_distance,
"saturation-distance", (gdouble) saturation,
"value-distance", (gdouble) value,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Noise HSV"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_illusion {
$blurb = 'Superimpose many altered copies of the image';
$help = <<'HELP';
Produce illusion.
HELP
&std_pdb_compat('gegl:illusion');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'division', type => '0 <= int32 <= 64',
desc => 'The number of divisions' },
{ name => 'type', type => '0 <= int32 <= 1',
desc => 'Illusion type { TYPE1 (0), TYPE2 (1) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:illusion",
"division", (gint) division,
"illusion-type", (gint) type,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Illusion"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_laplace {
$blurb = 'High-resolution edge detection';
$help = <<'HELP';
This plug-in creates one-pixel wide edges from the
image, with the value proportional to the gradient.
It uses the Laplace operator (a 3x3 kernel with -8
in the middle). The image has to be laplacered to
get useful results, a gauss_iir with 1.5 - 5.0
depending on the noise in the image is best.
HELP
&std_pdb_compat('gegl:edge-laplace');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:edge-laplace",
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Laplace"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_lens_distortion {
$blurb = 'Corrects lens distortion';
$help = <<'HELP';
Corrects barrel or pincushion lens distortion.
HELP
&std_pdb_compat('gegl:lens-distortion');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'offset_x', type => '-100 <= float <= 100',
desc => 'Effect centre offset in X' },
{ name => 'offset_y', type => '-100 <= float <= 100',
desc => 'Effect centre offset in Y' },
{ name => 'main_adjust', type => '-100 <= float <= 100',
desc => 'Amount of second-order distortion' },
{ name => 'edge_adjust', type => '-100 <= float <= 100',
desc => 'Amount of fourth-order distortion' },
{ name => 'rescale', type => '-100 <= float <= 100',
desc => 'Rescale overall image size' },
{ name => 'brighten', type => '-100 <= float <= 100',
desc => 'Adjust brightness in corners' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node = NULL;
PikaRGB color;
GeglColor *gegl_color;
pika_context_get_background (context, &color);
if (pika_drawable_has_alpha (drawable))
{
pika_rgb_set_alpha (&color, 0.0);
}
else
{
pika_rgb_set_alpha (&color, 1.0);
}
gegl_color = pika_gegl_color_new (&color, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:lens-distortion",
"main", (gdouble) main_adjust,
"edge", (gdouble) edge_adjust,
"zoom", (gdouble) rescale,
"x-shift", (gdouble) offset_x,
"y-shift", (gdouble) offset_y,
"brighten", (gdouble) brighten,
"background", gegl_color,
NULL);
g_object_unref (gegl_color);
node = wrap_in_selection_bounds (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Lens Distortion"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_make_seamless {
$blurb = 'Alters edges to make the image seamlessly tileable';
$help = <<'HELP';
This plug-in creates a seamless tileable from the input drawable.
HELP
&std_pdb_compat('gegl:tile-seamless');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:tile-seamless",
NULL);
node = wrap_in_selection_bounds (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Tile Seamless"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_maze {
$blurb = 'Draw a labyrinth';
$help = <<'HELP';
Generates a maze using either the depth-first search method or Prim's
algorithm. Can make tileable mazes too.
HELP
&std_pdb_compat('gegl:maze');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'width', type => '1 <= int32 <= 1024',
desc => 'Width of the passages' },
{ name => 'height', type => '1 <= int32 <= 1024',
desc => 'Height of the passages' },
{ name => 'tileable', type => '0 <= uchar <= 1',
desc => 'Tileable maze? (TRUE or FALSE)' },
{ name => 'algorithm', type => '0 <= uchar <= 1',
desc => 'Generation algorithm (0 = DEPTH FIRST, 1 = PRIM\'S ALGORITHM)' },
{ name => 'seed', type => 'int32',
desc => 'Random Seed' },
{ name => 'multiple', type => 'int32', dead => 1,
desc => 'Multiple (use 57)' },
{ name => 'offset', type => 'int32', dead => 1,
desc => 'Offset (use 1)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglColor *fg_color;
GeglColor *bg_color;
PikaRGB color;
pika_context_get_foreground (context, &color);
fg_color = pika_gegl_color_new (&color, NULL);
pika_context_get_background (context, &color);
bg_color = pika_gegl_color_new (&color, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:maze",
"x", width,
"y", height,
"algorithm-type", algorithm,
"tileable", tileable,
"seed", seed,
"fg-color", fg_color,
"bg-color", bg_color,
NULL);
g_object_unref (fg_color);
g_object_unref (bg_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Maze"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_mblur {
$blurb = 'Simulate movement using directional blur';
$help = <<'HELP';
This plug-in simulates the effect seen when
photographing a moving object at a slow shutter
speed. Done by adding multiple displaced copies.
HELP
&std_pdb_compat('gegl:motion-blur-linear, -zoom, -circular');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'type', type => '0 <= int32 <= 2',
desc => 'Type of motion blur { LINEAR (0), RADIAL (1), ZOOM (2) }' },
{ name => 'length', type => 'float',
desc => 'Length' },
{ name => 'angle', type => '0 <= float <= 360',
desc => 'Angle' },
{ name => 'center_x', type => 'float',
desc => 'Center X' },
{ name => 'center_y', type => 'float',
desc => 'Center Y' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node = NULL;
gint width = pika_item_get_width (PIKA_ITEM (drawable));
gint height = pika_item_get_height (PIKA_ITEM (drawable));
center_x /= (gdouble) width;
center_y /= (gdouble) height;
if (angle > 180.0)
angle -= 360.0;
if (type == 0)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-linear",
"length", length,
"angle", angle,
NULL);
}
else if (type == 1)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-circular",
"center-x", center_x,
"center-y", center_y,
"angle", angle,
NULL);
}
else if (type == 2)
{
gdouble factor = CLAMP (length / 256.0, 0.0, 1.0);
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-zoom",
"center-x", center_x,
"center-y", center_y,
"factor", factor,
NULL);
}
if (node != NULL)
{
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Motion Blur"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_mblur_inward {
$blurb = 'Simulate movement using directional blur';
$help = <<'HELP';
This procedure is equivalent to plug-in-mblur but
performs the zoom blur inward instead of outward.
HELP
&std_pdb_compat('gegl:motion-blur-linear, -zoom, -circular');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'type', type => '0 <= int32 <= 2',
desc => 'Type of motion blur { LINEAR (0), RADIAL (1), ZOOM (2) }' },
{ name => 'length', type => 'float',
desc => 'Length' },
{ name => 'angle', type => '0 <= float <= 360',
desc => 'Angle' },
{ name => 'center_x', type => 'float',
desc => 'Center X' },
{ name => 'center_y', type => 'float',
desc => 'Center Y' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node = NULL;
gint width = pika_item_get_width (PIKA_ITEM (drawable));
gint height = pika_item_get_height (PIKA_ITEM (drawable));
center_x /= (gdouble) width;
center_y /= (gdouble) height;
if (type == 0)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-linear",
"length", length,
"angle", angle,
NULL);
}
else if (type == 1)
{
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-circular",
"center-x", center_x,
"center-y", center_y,
"angle", angle,
NULL);
}
else if (type == 2)
{
gdouble factor = CLAMP (-length / (256.0 - length), -10.0, 0.0);
node = gegl_node_new_child (NULL,
"operation", "gegl:motion-blur-zoom",
"center-x", center_x,
"center-y", center_y,
"factor", factor,
NULL);
}
if (node != NULL)
{
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Motion Blur"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_median_blur {
$blurb = 'Blur using the median color near each pixel';
$help = <<'HELP';
Blur resulting from computing the median color in the
neighborhood of each pixel
HELP
&std_pdb_compat('gegl:median-blur');
$date = '2021';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '-400 <= int32 <= 400',
desc => 'Neighborhood radius, a negative value will calculate with inverted percentiles' },
{ name => 'percentile', type => '0 <= float <= 100',
desc => 'Neighborhood color percentile' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:median-blur",
"radius", radius,
"percentile", percentile,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Median Blur"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_mosaic {
$blurb = 'Convert the image into irregular tiles';
$help = <<'HELP';
Mosaic is a filter which transforms an image into
what appears to be a mosaic, composed of small primitives,
each of constant color and of an approximate size.
HELP
&std_pdb_compat('gegl:mosaic');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'tile_size', type => '1 <= float <= 1000',
desc => 'Average diameter of each tile (in pixels)' },
{ name => 'tile_height', type => '1 <= float <= 1000',
desc => 'Apparent height of each tile (in pixels)' },
{ name => 'tile_spacing', type => '0.1 <= float <= 1000',
desc => 'Inter_tile spacing (in pixels)' },
{ name => 'tile_neatness', type => '0 <= float <= 1.0',
desc => 'Deviation from perfectly formed tiles' },
{ name => 'tile_allow_split', type => '0 <= int32 <= 1',
desc => 'Allows splitting tiles at hard edges' },
{ name => 'light_dir', type => '0 <= float <= 360',
desc => 'Direction of light_source (in degrees)' },
{ name => 'color_variation', type => '0.0 <= float <= 1.0',
desc => 'Magnitude of random color variations' },
{ name => 'antialiasing', type => '0 <= int32 <= 1',
desc => 'Enables smoother tile output at the cost of speed' },
{ name => 'color_averaging', type => '0 <= int32 <= 1',
desc => 'Tile color based on average of subsumed pixels' },
{ name => 'tile_type', type => '0 <= int32 <= 3',
desc => 'Tile geometry { SQUARES (0), HEXAGONS (1), OCTAGONS (2), TRIANGLES (3) }' },
{ name => 'tile_surface', type => '0 <= int32 <= 1',
desc => 'Surface characteristics { SMOOTH (0), ROUGH (1) }' },
{ name => 'grout_color', type => '0 <= int32 <= 1',
desc => 'Grout color (black/white or fore/background) { BW (0), FG-BG (1) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglColor *fg_color;
GeglColor *bg_color;
GeglNode *node;
if (grout_color)
{
PikaRGB fgcolor, bgcolor;
pika_context_get_background (context, &bgcolor);
bg_color = pika_gegl_color_new (&bgcolor, NULL);
pika_context_get_foreground (context, &fgcolor);
fg_color = pika_gegl_color_new (&fgcolor, NULL);
}
else
{
/* sic */
fg_color = gegl_color_new ("white");
bg_color = gegl_color_new ("black");
}
node = gegl_node_new_child (NULL,
"operation", "gegl:mosaic",
"tile-size", (gdouble) tile_size,
"tile-height", (gdouble) tile_height,
"tile-spacing", (gdouble) tile_spacing,
"tile-neatness", (gdouble) tile_neatness,
"tile-allow-split", (gboolean) tile_allow_split,
"light-dir", (gdouble) light_dir,
"color-variation", (gfloat) color_variation,
"antialiasing", (gboolean) antialiasing,
"color-averaging", (gboolean) color_averaging,
"tile-type", (gint) tile_type,
"tile-surface", (gboolean) tile_surface,
"light-color", fg_color,
"joints-color", bg_color,
NULL);
g_object_unref (fg_color);
g_object_unref (bg_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Mosaic"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_neon {
$blurb = 'Simulate the glowing boundary of a neon light';
$help = <<'HELP';
This filter works in a manner similar to the edge plug-in, but uses
the first derivative of the gaussian operator to achieve resolution
independence. The IIR method of calculating the effect is utilized to
keep the processing time constant between large and small standard
deviations.
HELP
&std_pdb_compat('gegl:edge-neon');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '0.0 <= float <= 1500.0',
desc => 'Radius of neon effect (in pixels)' },
{ name => 'amount', type => '0.0 <= float <= 100.0',
desc => 'Effect enhancement variable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:edge-neon",
"radius", radius,
"amount", amount,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Neon"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_newsprint {
$blurb = 'Halftone the image to give newspaper-like effect';
$help = $blurb;
&std_pdb_compat('gegl:newsprint');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'cell_width', type => '0 <= int32 <= 1500',
desc => 'Screen cell width in pixels' },
{ name => 'colorspace', type => '0 <= int32 <= 3',
desc => 'Separate to { GRAYSCALE (0), RGB (1), CMYK (2), LUMINANCE (3) }' },
{ name => 'k_pullout', type => '0 <= int32 <= 100',
desc => 'Percentage of black to pullout (CMYK only)' },
{ name => 'gry_ang', type => '0.0 <= float <= 360.0',
desc => 'Grey/black screen angle (degrees)' },
{ name => 'gry_spotfn', type => '0 <= int32 <= 4',
desc => 'Grey/black spot function { DOTS (0), LINES (1), DIAMONDS (2), EUCLIDIAN-DOT (3), PS-DIAMONDS (4) }' },
{ name => 'red_ang', type => '0.0 <= float <= 360.0',
desc => 'Red/cyan screen angle (degrees)' },
{ name => 'red_spotfn', type => '0 <= int32 <= 4',
desc => 'Red/cyan spot function { DOTS (0), LINES (1), DIAMONDS (2), EUCLIDIAN-DOT (3), PS-DIAMONDS (4) }' },
{ name => 'grn_ang', type => '0.0 <= float <= 360.0',
desc => 'Green/magenta screen angle (degrees)' },
{ name => 'grn_spotfn', type => '0 <= int32 <= 4',
desc => 'Green/magenta spot function { DOTS (0), LINES (1), DIAMONDS (2), EUCLIDIAN-DOT (3), PS-DIAMONDS (4) }' },
{ name => 'blu_ang', type => '0.0 <= float <= 360.0',
desc => 'Blue/yellow screen angle (degrees)' },
{ name => 'blu_spotfn', type => '0 <= int32 <= 4',
desc => 'Blue/yellow spot function { DOTS (0), LINES (1), DIAMONDS (2), EUCLIDIAN-DOT (3), PS-DIAMONDS (4) }' },
{ name => 'oversample', type => '0 <= int32 <= 128',
desc => 'how many times to oversample spot fn' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gint color_model = newsprint_color_model (colorspace);
gint pattern = newsprint_pattern (gry_spotfn);
gint pattern2 = newsprint_pattern (red_spotfn);
gint pattern3 = newsprint_pattern (grn_spotfn);
gint pattern4 = newsprint_pattern (blu_spotfn);
gdouble angle = newsprint_angle (gry_ang);
gdouble angle2 = newsprint_angle (red_ang);
gdouble angle3 = newsprint_angle (grn_ang);
gdouble angle4 = newsprint_angle (blu_ang);
node = gegl_node_new_child (NULL,
"operation", "gegl:newsprint",
"color-model", color_model,
"black-pullout", (gdouble) k_pullout / 100.0,
"period", (gdouble) cell_width,
"angle", angle,
"pattern", pattern,
"period2", (gdouble) cell_width,
"angle2", angle2,
"pattern2", pattern2,
"period3", (gdouble) cell_width,
"angle3", angle3,
"pattern3", pattern3,
"period4", (gdouble) cell_width,
"angle4", angle4,
"pattern4", pattern4,
"aa-samples", oversample,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Newsprint"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_normalize {
$blurb = 'Stretch brightness values to cover the full range';
$help = <<'HELP';
This plug-in performs almost the same operation as the 'contrast
autostretch' plug-in, except that it won't allow the color channels to
normalize independently. This is actually what most people probably
want instead of contrast-autostretch; use c-a only if you wish to
remove an undesirable color-tint from a source image which is supposed
to contain pure-white and pure-black.
HELP
&std_pdb_compat('gegl:stretch-contrast');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:stretch-contrast",
"keep-colors", TRUE,
"perceptual", TRUE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Normalize"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_nova {
$blurb = 'Add a starburst to the image';
$help = <<'HELP';
This plug-in produces an effect like a supernova burst. The amount of
the light effect is approximately in proportion to 1/r, where r is the
distance from the center of the star.
HELP
&std_pdb_compat('gegl:supernova');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'xcenter', type => 'int32',
desc => 'X coordinates of the center of supernova' },
{ name => 'ycenter', type => 'int32',
desc => 'Y coordinates of the center of supernova' },
{ name => 'color', type => 'color',
desc => 'Color of supernova' },
{ name => 'radius', type => '1 <= int32 <= 3000',
desc => 'Radius of supernova' },
{ name => 'nspoke', type => '1 <= int32 <= 1024',
desc => 'Number of spokes' },
{ name => 'randomhue', type => '0 <= int32 <= 360',
desc => 'Random hue' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglColor *gegl_color = pika_gegl_color_new (&color, NULL);
gdouble center_x = (gdouble) xcenter / (gdouble) pika_item_get_width (PIKA_ITEM (drawable));
gdouble center_y = (gdouble) ycenter / (gdouble) pika_item_get_height (PIKA_ITEM (drawable));
node = gegl_node_new_child (NULL,
"operation", "gegl:supernova",
"center-x", center_x,
"center-y", center_y,
"radius", radius,
"spokes-count", nspoke,
"random-hue", randomhue,
"color", gegl_color,
"seed", g_random_int (),
NULL);
g_object_unref (gegl_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Supernova"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_oilify {
$blurb = 'Smear colors to simulate an oil painting';
$help = <<'HELP';
This function performs the well-known oil-paint effect on the
specified drawable.
HELP
&std_pdb_compat('gegl:oilify');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'mask_size', type => '1 <= int32 <= 200',
desc => 'Oil paint mask size' },
{ name => 'mode', type => '0 <= int32 <= 1',
desc => 'Algorithm { RGB (0), INTENSITY (1) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:oilify",
"mask-radius", MAX (1, mask_size / 2),
"use-inten", mode ? TRUE : FALSE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Oilify"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_oilify_enhanced {
$blurb = 'Smear colors to simulate an oil painting';
$help = <<'HELP';
This function performs the well-known oil-paint effect on the
specified drawable.
HELP
&std_pdb_compat('gegl:oilify');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'mode', type => '0 <= int32 <= 1',
desc => 'Algorithm { RGB (0), INTENSITY (1) }' },
{ name => 'mask_size', type => '1 <= int32 <= 200',
desc => 'Oil paint mask size' },
{ name => 'mask_size_map', type => 'drawable', none_ok => 1,
desc => 'Mask size control map' },
{ name => 'exponent', type => '1 <= int32 <= 20',
desc => 'Oil paint exponent' },
{ name => 'exponent_map', type => 'drawable', none_ok => 1,
desc => 'Exponent control map' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *graph;
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:oilify",
"mask-radius", MAX (1, mask_size / 2),
"use-inten", mode ? TRUE : FALSE,
"exponent", exponent,
NULL);
graph = wrap_in_graph (node);
if (mask_size_map)
{
GeglNode *src_node;
src_node = create_buffer_source_node (graph, mask_size_map);
gegl_node_connect (src_node, "output", node, "aux");
}
if (exponent_map)
{
GeglNode *src_node;
src_node = create_buffer_source_node (graph, exponent_map);
gegl_node_connect (src_node, "output", node, "aux2");
}
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Oilify"),
graph);
g_object_unref (graph);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_papertile {
$blurb = 'Cut image into paper tiles, and slide them';
$help = <<'HELP';
This plug-in cuts an image into paper tiles and slides each paper tile.
HELP
&std_pdb_compat('gegl:tile-paper');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'tile_size', type => 'int32',
desc => 'Tile size (pixels)' },
{ name => 'move_max', type => 'float',
desc => 'Max move rate (%)' },
{ name => 'fractional_type', type => '0 <= int32 <= 2',
desc => 'Fractional type { BACKGROUND (0), IGNORE (1), FORCE (2) }' },
{ name => 'wrap_around', type => 'boolean',
desc => 'Wrap around' },
{ name => 'centering', type => 'boolean',
desc => 'Centering' },
{ name => 'background_type', type => '0 <= int32 <= 5',
desc => 'Background type { TRANSPARENT (0), INVERTED (1), IMAGE (2), FG (3), BG (4), COLOR (5) }' },
{ name => 'background_color', type => 'color',
desc => 'Background color (for background-type == 5)' },
{ name => 'background_alpha', type => 'int32', dead => 1,
desc => 'Background alpha (unused)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
PikaRGB color;
GeglColor *gegl_color;
gint bg_type;
switch (background_type)
{
default:
bg_type = background_type;
pika_rgba_set (&color, 0.0, 0.0, 1.0, 1.0);
break;
case 3:
bg_type = 3;
pika_context_get_foreground (context, &color);
break;
case 4:
bg_type = 3;
pika_context_get_background (context, &color);
break;
case 5:
bg_type = 3;
color = background_color;
break;
}
gegl_color = pika_gegl_color_new (&color, NULL);
node = gegl_node_new_child (NULL,
"operation", "gegl:tile-paper",
"tile-width", tile_size,
"tile-height", tile_size,
"move-rate", move_max,
"bg-color", gegl_color,
"centering", centering,
"wrap-around", wrap_around,
"background-type", bg_type,
"fractional-type", fractional_type,
NULL);
g_object_unref (gegl_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Paper Tile"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_photocopy {
$blurb = 'Simulate color distortion produced by a copy machine';
$help = <<'HELP';
Propagates dark values in an image based on each pixel's relative
darkness to a neighboring average. The idea behind this filter is to
give the look of a photocopied version of the image, with toner
transferred based on the relative darkness of a particular
region. This is achieved by darkening areas of the image which are
measured to be darker than a neighborhood average and setting other
pixels to white. In this way, sufficiently large shifts in intensity
are darkened to black. The rate at which they are darkened to black is
determined by the second pct_black parameter. The mask_radius
parameter controls the size of the pixel neighborhood over which the
average intensity is computed and then compared to each pixel in the
neighborhood to decide whether or not to darken it to black. Large
values for mask_radius result in very thick black areas bordering the
regions of white and much less detail for black areas everywhere
including inside regions of color. Small values result in less toner
overall and more detail everywhere. Small values for the pct_black
make the blend from the white regions to the black border lines
smoother and the toner regions themselves thinner and less noticeable;
larger values achieve the opposite effect.
HELP
&std_pdb_compat('gegl:photocopy');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'mask_radius', type => '3.0 <= float <= 50.0',
desc => 'Photocopy mask radius (radius of pixel neighborhood)' },
{ name => 'sharpness', type => '0.0 <= float <= 1.0',
desc => 'Sharpness (detail level)' },
{ name => 'pct_black', type => '0.0 <= float <= 1.0',
desc => 'Percentage of darkened pixels to set to black' },
{ name => 'pct_white', type => '0.0 <= float <= 1.0',
desc => 'Percentage of non-darkened pixels left white' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:photocopy",
"mask-radius", mask_radius,
"sharpness", sharpness,
"black", pct_black,
"white", pct_white,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Photocopy"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_pixelize {
$blurb = 'Simplify image into an array of solid-colored squares';
$help = <<'HELP';
Pixelize the contents of the specified drawable with specified
pixelizing width.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'pixel_width', type => '1 <= int32 <= PIKA_MAX_IMAGE_SIZE',
desc => 'Pixel width (the decrease in resolution)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:pixelize",
"size-x", pixel_width,
"size-y", pixel_width,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Pixelize"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_pixelize2 {
$blurb = 'Simplify image into an array of solid-colored rectangles';
$help = <<'HELP';
Pixelize the contents of the specified drawable with specified
pixelizing width and height.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'pixel_width', type => '1 <= int32 <= PIKA_MAX_IMAGE_SIZE',
desc => 'Pixel width (the decrease in horizontal resolution)' },
{ name => 'pixel_height', type => '1 <= int32 <= PIKA_MAX_IMAGE_SIZE',
desc => 'Pixel height (the decrease in vertical resolution)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:pixelize",
"size-x", pixel_width,
"size-y", pixel_height,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Pixelize"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_plasma {
$blurb = 'Create a random plasma texture';
$help = <<'HELP';
This plug-in produces plasma fractal images.
HELP
&std_pdb_compat('gegl:plasma');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'seed', type => '-1 <= int32 <= G_MAXINT',
desc => 'Random seed' },
{ name => 'turbulence', type => '0.0 <= float <= 7.0',
desc => 'The value of the turbulence' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gint x, y, width, height;
pika_item_mask_intersect (PIKA_ITEM (drawable), &x, &y, &width, &height);
node = gegl_node_new_child (NULL,
"operation", "gegl:plasma",
"seed", seed,
"turbulence", turbulence,
"x", x,
"y", y,
"width", width,
"height", height,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Plasma"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_polar_coords {
$blurb = 'Convert image to or from polar coordinates';
$help = <<'HELP';
Remaps and image from rectangular coordinates to polar coordinates or
vice versa.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'circle', type => '0.0 <= float <= 100.0',
desc => 'Circle depth in %' },
{ name => 'angle', type => '0.0 <= float < 360.0',
desc => 'Offset angle' },
{ name => 'backwards', type => 'boolean',
desc => 'Map backwards' },
{ name => 'inverse', type => 'boolean',
desc => 'Map from top' },
{ name => 'polrec', type => 'boolean',
desc => 'Polar to rectangular' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:polar-coordinates",
"depth", circle,
"angle", angle,
"bw", backwards, /* XXX name */
"top", inverse,
"polar", polrec,
NULL);
node = wrap_in_selection_bounds (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Polar Coordinates"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_randomize_hurl {
$blurb = 'Completely randomize a fraction of pixels';
$help = <<'HELP';
This plug-in "hurls" randomly-valued pixels onto the selection or
image. You may select the percentage of pixels to modify and the
number of times to repeat the process.
HELP
&std_pdb_compat('gegl:noise-hurl');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'rndm_pct', type => '0.0 <= float <= 100.0',
desc => 'Randomization percentage' },
{ name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
desc => 'Repeat count' },
{ name => 'randomize', type => 'boolean',
desc => 'Use random seed' },
{ name => 'seed', type => 'int32',
desc => 'Seed value (used only if randomize is FALSE)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
if (randomize)
seed = (gint32) g_random_int ();
node =
gegl_node_new_child (NULL,
"operation", "gegl:noise-hurl",
"seed", seed,
"pct-random", rndm_pct,
"repeat", (gint) rndm_rcount,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Random Hurl"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_randomize_pick {
$blurb = 'Randomly interchange some pixels with neighbors';
$help = <<'HELP';
This plug-in replaces a pixel with a random adjacent pixel. You may
select the percentage of pixels to modify and the number of times to
repeat the process.
HELP
&std_pdb_compat('gegl:noise-pick');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'rndm_pct', type => '1.0 <= float <= 100.0',
desc => 'Randomization percentage' },
{ name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
desc => 'Repeat count' },
{ name => 'randomize', type => 'boolean',
desc => 'Use random seed' },
{ name => 'seed', type => 'int32',
desc => 'Seed value (used only if randomize is FALSE)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
if (randomize)
seed = (gint32) g_random_int ();
node =
gegl_node_new_child (NULL,
"operation", "gegl:noise-pick",
"seed", seed,
"pct-random", rndm_pct,
"repeat", (gint) rndm_rcount,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Random Pick"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_randomize_slur {
$blurb = 'Randomly slide some pixels downward (similar to melting';
$help = <<'HELP';
This plug-in "slurs" (melts like a bunch of icicles) an image. You may
select the percentage of pixels to modify and the number of times to
repeat the process.
HELP
&std_pdb_compat('gegl:noise-slur');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'rndm_pct', type => '1.0 <= float <= 100.0',
desc => 'Randomization percentage' },
{ name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
desc => 'Repeat count' },
{ name => 'randomize', type => 'boolean',
desc => 'Use random seed' },
{ name => 'seed', type => 'int32',
desc => 'Seed value (used only if randomize is FALSE)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
if (randomize)
seed = (gint32) g_random_int ();
node =
gegl_node_new_child (NULL,
"operation", "gegl:noise-slur",
"seed", seed,
"pct-random", rndm_pct,
"repeat", (gint) rndm_rcount,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Random Slur"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_red_eye_removal {
$blurb = 'Remove the red eye effect caused by camera flashes';
$help = <<'HELP';
This procedure removes the red eye effect caused by camera flashes by
using a percentage based red color threshold. Make a selection
containing the eyes, and apply the filter while adjusting the
threshold to accurately remove the red eyes.
HELP
&std_pdb_compat('gegl:red-eye-removal');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'threshold', type => '0 <= int32 <= 100',
desc => 'Red eye threshold in percent' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:red-eye-removal",
"threshold", (gdouble) (threshold - 50) / 50.0 * 0.2 + 0.4,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Red Eye Removal"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_rgb_noise {
$blurb = 'Distort colors by random amounts';
$help = <<'HELP';
Add normally distributed (zero mean) random values to image channels.
Noise may be additive (uncorrelated) or multiplicative (correlated -
also known as speckle noise). For color images color channels may be
treated together or independently.
HELP
&std_pdb_compat('gegl:noise-rgb');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'independent', type => 'boolean',
desc => 'Noise in channels independent' },
{ name => 'correlated', type => 'boolean',
desc => 'Noise correlated (i.e. multiplicative not additive)' },
{ name => 'noise_1', type => '0.0 <= float <= 1.0',
desc => 'Noise in the first channel (red, gray)' },
{ name => 'noise_2', type => '0.0 <= float <= 1.0',
desc => 'Noise in the second channel (green, gray_alpha)' },
{ name => 'noise_3', type => '0.0 <= float <= 1.0',
desc => 'Noise in the third channel (blue)' },
{ name => 'noise_4', type => '0.0 <= float <= 1.0',
desc => 'Noise in the fourth channel (alpha)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gdouble r, g, b, a;
if (pika_drawable_is_gray (drawable))
{
r = noise_1;
g = noise_1;
b = noise_1;
a = noise_2;
}
else
{
r = noise_1;
g = noise_2;
b = noise_3;
a = noise_4;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:noise-rgb",
"correlated", correlated,
"independent", independent,
"red", r,
"green", g,
"blue", b,
"alpha", a,
"seed", g_random_int (),
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "RGB Noise"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_ripple {
$blurb = 'Displace pixels in a ripple pattern';
$help = <<'HELP';
Ripples the pixels of the specified drawable.
Each row or column will be displaced a certain number
of pixels coinciding with the given wave form.
HELP
&std_pdb_compat('gegl:ripple');
$date = '2018';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'period', type => ' 0.0 < float < 1000.0',
default => 200.0, desc => 'Period: number of pixels for one wave to complete' },
{ name => 'amplitude', type => ' 0.0 < float < 1000.0',
default => 25.0, desc => 'Amplitude: maximum displacement of wave' },
{ name => 'orientation', type => '0 <= int32 <= 1',
desc => 'Orientation { ORIENTATION-HORIZONTAL (0), ORIENTATION-VERTICAL (1) }' },
{ name => 'edges', type => '0 <= int32 <= 2',
desc => 'Edges { SMEAR (0), WRAP (1), BLANK (2) }' },
{ name => 'waveform', type => '0 <= int32 <= 1',
desc => 'Waveform { SAWTOOTH (0), SINE (1) }' },
{ name => 'antialias', type => 'boolean',
desc => 'Antialias { TRUE, FALSE }' },
{ name => 'tile', type => 'boolean',
desc => 'Tileable { TRUE, FALSE }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gdouble angle, phi;
angle = orientation ? 0.0 : 90.0;
phi = waveform ? 0.0 : 0.75;
if (orientation == 0 && waveform == 1)
phi = 0.5;
node = gegl_node_new_child (NULL,
"operation", "gegl:ripple",
"amplitude", amplitude,
"period", period,
"phi", phi,
"angle", angle,
"sampler_type", antialias ? GEGL_SAMPLER_CUBIC : GEGL_SAMPLER_NEAREST,
"wave_type", waveform ? 0 : 1,
"abyss_policy", edges == 0 ? GEGL_ABYSS_CLAMP :
edges == 1 ? GEGL_ABYSS_LOOP :
GEGL_ABYSS_NONE,
"tileable", tile ? TRUE : FALSE,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Ripple"),
node);
g_object_unref (node);
}
else
{
success = FALSE;
}
}
CODE
);
}
sub plug_in_rotate {
$blurb = 'Rotates a layer or the whole image by 90, 180 or 270 degrees';
$help = <<'HELP';
This plug-in does rotate the active layer or the whole image clockwise
by multiples of 90 degrees. When the whole image is chosen, the image
is resized if necessary.
HELP
&neo_pdb_misc;
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image',
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'angle', type => '1 <= int32 <= 3',
desc => 'Angle { 90 (1), 180 (2), 270 (3) } degrees' },
{ name => 'everything', type => 'boolean',
desc => 'Rotate the whole image' }
);
%invoke = (
code => <<'CODE'
{
PikaRotationType rotate_type = angle - 1;
if (everything)
{
pika_image_rotate (image, context, rotate_type, progress);
}
else if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error))
{
PikaItem *item = PIKA_ITEM (drawable);
gint off_x, off_y;
gdouble center_x, center_y;
pika_item_get_offset (item, &off_x, &off_y);
center_x = ((gdouble) off_x + (gdouble) pika_item_get_width (item) / 2.0);
center_y = ((gdouble) off_y + (gdouble) pika_item_get_height (item) / 2.0);
pika_item_rotate (item, context, rotate_type, center_x, center_y,
PIKA_IS_CHANNEL (drawable));
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_noisify {
$blurb = 'Adds random noise to image channels';
$help = <<'HELP';
Add normally distributed random values to image channels. For color
images each color channel may be treated together or independently.
HELP
&std_pdb_compat('gegl:noise-rgb');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'independent', type => 'boolean',
desc => 'Noise in channels independent' },
{ name => 'noise_1', type => '0.0 <= float <= 1.0',
desc => 'Noise in the first channel (red, gray)' },
{ name => 'noise_2', type => '0.0 <= float <= 1.0',
desc => 'Noise in the second channel (green, gray_alpha)' },
{ name => 'noise_3', type => '0.0 <= float <= 1.0',
desc => 'Noise in the third channel (blue)' },
{ name => 'noise_4', type => '0.0 <= float <= 1.0',
desc => 'Noise in the fourth channel (alpha)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gdouble r, g, b, a;
if (pika_drawable_is_gray (drawable))
{
r = noise_1;
g = noise_1;
b = noise_1;
a = noise_2;
}
else
{
r = noise_1;
g = noise_2;
b = noise_3;
a = noise_4;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:noise-rgb",
"correlated", FALSE,
"independent", independent,
"red", r,
"green", g,
"blue", b,
"alpha", a,
"seed", g_random_int (),
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Noisify"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_sel_gauss {
$blurb = 'Blur neighboring pixels, but only in low-contrast areas';
$help = <<'HELP';
This filter functions similar to the regular gaussian blur filter
except that neighbouring pixels that differ more than the given
maxdelta parameter will not be blended with. This way with the correct
parameters, an image can be smoothed out without losing
details. However, this filter can be rather slow.
HELP
&std_pdb_compat('gegl:gaussian-blur-selective');
$date = '2099';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '0.0 < float',
desc => 'Radius of gaussian blur (in pixels)' },
{ name => 'max_delta', type => '0 <= int32 <= 255',
desc => 'Maximum delta' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:gaussian-blur-selective",
"blur-radius", radius,
"max-delta", (gdouble) max_delta / 255.0,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Selective Gaussian Blur"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_semiflatten {
$blurb = 'Replace partial transparency with the current background color';
$help = <<'HELP';
This plug-in flattens pixels in an RGBA image that aren't completely
transparent against the current PIKA background color.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error) &&
pika_drawable_has_alpha (drawable))
{
GeglNode *node;
PikaRGB color;
pika_context_get_background (context, &color);
node =
gegl_node_new_child (NULL,
"operation", "pika:semi-flatten",
"color", &color,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Semi-Flatten"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_shift {
$blurb = 'Shift each row or column of pixels by a random amount';
$help = <<'HELP';
Shifts the pixels of the specified drawable. Each row or column will
be displaced a random value of pixels.
HELP
&std_pdb_compat('gegl:shift');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'shift_amount', type => '0 <= int32 <= 200',
desc => 'Shift amount' },
{ name => 'orientation', type => '0 <= int32 <= 1',
desc => 'Orientation { ORIENTATION-VERTICAL (0), ORIENTATION-HORIZONTAL (1) }' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:shift",
"shift", shift_amount / 2,
"direction", orientation ? 0 : 1,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Shift"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_sinus {
$blurb = 'Generate complex sinusoidal textures';
$help = 'FIXME: sinus help',
&std_pdb_compat('gegl:sinus');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'xscale', type => '0 <= float',
desc => 'Scale value for x axis' },
{ name => 'yscale', type => '0 <= float',
desc => 'Scale value for y axis' },
{ name => 'complex', type => '0 <= float',
desc => 'Complexity factor' },
{ name => 'seed', type => '0 <= int32',
desc => 'Seed value for random number generator' },
{ name => 'tiling', type => 'boolean',
desc => 'If set, the pattern generated will tile' },
{ name => 'perturb', type => 'boolean',
desc => 'If set, the pattern is a little more distorted...' },
{ name => 'colors', type => '0 <= int32 <=2',
desc => 'where to take the colors (0=B&W, 1=fg/bg, 2=col1/col2)' },
{ name => 'col1', type => 'color',
desc => 'fist color (sometimes unused)' },
{ name => 'col2', type => 'color',
desc => 'second color (sometimes unused)' },
{ name => 'alpha1', type => '0 <= float <= 1',
desc => 'alpha for the first color (used if the drawable has an alpha channel)' },
{ name => 'alpha2', type => '0 <= float <= 1',
desc => 'alpha for the second color (used if the drawable has an alpha channel)' },
{ name => 'blend', type => '0 <= int32 <= 2',
desc => '0=linear, 1=bilinear, 2=sinusoidal' },
{ name => 'blend_power', type => 'float',
desc => 'Power used to stretch the blend' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
GeglColor *gegl_color1;
GeglColor *gegl_color2;
gint x, y, width, height;
switch (colors)
{
case 0:
pika_rgb_set (&col1, 0.0, 0.0, 0.0);
pika_rgb_set (&col2, 1.0, 1.0, 1.0);
break;
case 1:
pika_context_get_foreground (context, &col1);
pika_context_get_background (context, &col2);
break;
}
pika_rgb_set_alpha (&col1, alpha1);
pika_rgb_set_alpha (&col2, alpha2);
gegl_color1 = pika_gegl_color_new (&col1, NULL);
gegl_color2 = pika_gegl_color_new (&col2, NULL);
pika_item_mask_intersect (PIKA_ITEM (drawable), &x, &y, &width, &height);
node = gegl_node_new_child (NULL,
"operation", "gegl:sinus",
"x_scale", xscale,
"y-scale", yscale,
"complexity", complex,
"seed", seed,
"tiling", tiling,
"perturbation", perturb,
"color1", gegl_color1,
"color2", gegl_color2,
"blend-mode", blend,
"blend-power", blend_power,
"width", width,
"height", height,
NULL);
g_object_unref (gegl_color1);
g_object_unref (gegl_color2);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Sinus"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_sobel {
$blurb = 'Specialized direction-dependent edge detection';
$help = <<'HELP';
This plug-in calculates the gradient with a sobel operator. The user
can specify which direction to use. When both directions are used, the
result is the RMS of the two gradients; if only one direction is used,
the result either the absolute value of the gradient, or 127 +
gradient (if the 'keep sign' switch is on). This way, information
about the direction of the gradient is preserved. Resulting images are
not autoscaled."
HELP
&std_pdb_compat('gegl:edge-sobel');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'horizontal', type => 'boolean',
desc => 'Sobel in horizontal direction' },
{ name => 'vertical', type => 'boolean',
desc => 'Sobel in vertical direction' },
{ name => 'keep_sign', type => 'boolean',
desc => 'Keep sign of result (one direction only)' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:edge-sobel",
"horizontal", horizontal,
"vertical", vertical,
"keep-sign", keep_sign,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Sobel"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_softglow {
$blurb = 'Simulate glow by making highlights intense and fuzzy';
$help = <<'HELP';
Gives an image a softglow effect by intensifying the highlights in the
image. This is done by screening a modified version of the drawable
with itself. The modified version is desaturated and then a sigmoidal
transfer function is applied to force the distribution of intensities
into very small and very large only. This desaturated version is then
blurred to give it a fuzzy 'vaseline-on-the-lens' effect. The glow
radius parameter controls the sharpness of the glow effect. The
brightness parameter controls the degree of intensification applied to
image highlights. The sharpness parameter controls how defined or
alternatively, diffuse, the glow effect should be.
HELP
&std_pdb_compat('gegl:softglow');
$date = '2019';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'glow_radius', type => '0 <= float',
desc => 'Glow radius in pixels' },
{ name => 'brightness', type => '0.0 <= float <= 1.0',
desc => 'Glow brightness' },
{ name => 'sharpness', type => '0.0 <= float <= 1.0',
desc => 'Glow sharpness' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:softglow",
"glow-radius", glow_radius,
"brightness", brightness,
"sharpness", sharpness,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Softglow"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_solid_noise {
$blurb = 'Create a random cloud-like texture';
$help = <<'HELP';
Generates 2D textures using Perlin's classic solid noise function.
HELP
&std_pdb_compat('gegl:noise-solid');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'tileable', type => 'boolean',
desc => 'Create a tileable output' },
{ name => 'turbulent', type => 'boolean',
desc => 'Make a turbulent noise' },
{ name => 'seed', type => 'int32',
desc => 'Random seed' },
{ name => 'detail', type => '0 <= int32 <= 15',
desc => 'Detail level' },
{ name => 'xsize', type => 'float',
desc => 'Horizontal texture size' },
{ name => 'ysize', type => 'float',
desc => 'Vertical texture size' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gint x, y, width, height;
pika_item_mask_intersect (PIKA_ITEM (drawable), &x, &y, &width, &height);
node = gegl_node_new_child (NULL,
"operation", "gegl:noise-solid",
"x-size", xsize,
"y-size", ysize,
"detail", detail,
"tileable", tileable,
"turbulent", turbulent,
"seed", seed,
"width", width,
"height", height,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Solid Noise"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_spread {
$blurb = 'Move pixels around randomly';
$help = <<'HELP';
Spreads the pixels of the specified drawable. Pixels are randomly
moved to another location whose distance varies from the original by
the horizontal and vertical spread amounts.
HELP
&std_pdb_compat('gegl:noise-spread');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'spread_amount_x', type => '0 <= float <= 200',
desc => 'Horizontal spread amount' },
{ name => 'spread_amount_y', type => '0 <= float <= 200',
desc => 'Vertical spread amount' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:noise-spread",
"amount-x", (gint) spread_amount_x,
"amount-y", (gint) spread_amount_y,
"seed", g_random_int (),
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Spread"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_threshold_alpha {
$blurb = 'Make transparency all-or-nothing';
$help = <<'HELP';
Make transparency all-or-nothing.
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'threshold', type => '0 <= int32 <= 255',
desc => 'Threshold' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error) &&
pika_drawable_has_alpha (drawable))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "pika:threshold-alpha",
"value", threshold / 255.0,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Threshold Alpha"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_unsharp_mask {
$blurb = "The most widely useful method for sharpening an image";
$help = <<'HELP';
The unsharp mask is a sharpening filter that works by comparing using
the difference of the image and a blurred version of the image. It is
commonly used on photographic images, and is provides a much more
pleasing result than the standard sharpen filter.
HELP
&std_pdb_compat('gegl:unsharp-mask');
$date = '2018';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'radius', type => '0.0 <= float <= 300.0',
desc => 'Radius of gaussian blur' },
{ name => 'amount', type => '0.0 <= float <= 300.0',
desc => 'Strength of effect' },
{ name => 'threshold', type => '0 <= int32 <= 255',
desc => 'Threshold' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:unsharp-mask",
"std-dev", radius,
"scale", amount,
"threshold", threshold / 255.0,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Sharpen (Unsharp Mask)"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_video {
$blurb = 'Simulate distortion produced by a fuzzy or low-res monitor';
$help = <<'HELP';
This function simulates the degradation of being on an old
low-dotpitch RGB video monitor to the specified drawable.
HELP
&std_pdb_compat('gegl:video-degradation');
$date = '2014';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'pattern_number', type => '0 <= int32 <= 8',
desc => 'Type of RGB pattern to use' },
{ name => 'additive', type => 'boolean',
desc => 'Whether the function adds the result to the original image' },
{ name => 'rotated', type => 'boolean',
desc => 'Whether to rotate the RGB pattern by ninety degrees' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:video-degradation",
"pattern", pattern_number,
"additive", additive,
"rotated", rotated,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Video"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_vinvert {
$blurb = 'Invert the brightness of each pixel';
$help = <<'HELP';
This function takes an indexed/RGB image and inverts its 'value' in
HSV space. The upshot of this is that the color and saturation at any
given point remains the same, but its brightness is effectively
inverted. Quite strange. Sometimes produces unpleasant color
artifacts on images from lossy sources (ie. JPEG).
HELP
&std_pdb_misc;
$date = '1997';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:value-invert",
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Value Invert"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_vpropagate {
$blurb = 'Propagate certain colors to neighboring pixels',
$help = <<'HELP';
Propagate values of the layer.
HELP
&std_pdb_compat('gegl:value-propagate');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'propagate_mode', type => '0 <= int32 <= 7',
desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 5:background, 6:opaque, 7:transparent }' },
{ name => 'propagating_channel', type => 'int32',
desc => 'Channels which values are propagated' },
{ name => 'propagating_rate', type => '0.0 <= float <= 1.0',
desc => 'Propagating rate' },
{ name => 'direction_mask', type => '0 <= int32 <= 15',
desc => 'Direction mask' },
{ name => 'lower_limit', type => '0 <= int32 <= 255',
desc => 'Lower limit' },
{ name => 'upper_limit', type => '0 <= int32 <= 255',
desc => 'Upper limit' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
PikaRGB color;
GeglColor *gegl_color = NULL;
gint gegl_mode = 0;
gboolean to_left = (direction_mask & (0x1 << 0)) != 0;
gboolean to_top = (direction_mask & (0x1 << 1)) != 0;
gboolean to_right = (direction_mask & (0x1 << 2)) != 0;
gboolean to_bottom = (direction_mask & (0x1 << 3)) != 0;
gboolean value = (propagating_channel & (0x1 << 0)) != 0;
gboolean alpha = (propagating_channel & (0x1 << 1)) != 0;
switch (propagate_mode)
{
case 0:
case 1:
case 2:
gegl_mode = propagate_mode;
break;
case 3:
case 4:
case 5:
if (propagate_mode == 3 || propagate_mode == 4)
{
gegl_mode = propagate_mode;
pika_context_get_foreground (context, &color);
}
else
{
gegl_mode = 4;
pika_context_get_background (context, &color);
}
gegl_color = pika_gegl_color_new (&color, NULL);
break;
case 6:
case 7:
gegl_mode = propagate_mode - 1;
break;
}
node =
gegl_node_new_child (NULL,
"operation", "gegl:value-propagate",
"mode", gegl_mode,
"lower-threshold", (gdouble) lower_limit / 255.0,
"upper-threshold", (gdouble) upper_limit / 255.0,
"rate", propagating_rate,
"color", gegl_color,
"top", to_top,
"left", to_left,
"right", to_right,
"bottom", to_bottom,
"value", value,
"alpha", alpha,
NULL);
if (gegl_color)
g_object_unref (gegl_color);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Value Propagate"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_dilate {
$blurb = 'Grow lighter areas of the image',
$help = <<'HELP';
Dilate image.
HELP
&std_pdb_compat('gegl:value-propagate');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'propagate_mode', type => '0 <= int32 <= 7', dead => 1,
desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 5:background, 6:opaque, 7:transparent }' },
{ name => 'propagating_channel', type => 'int32', dead => 1,
desc => 'Channels which values are propagated' },
{ name => 'propagating_rate', type => '0.0 <= float <= 1.0', dead => 1,
desc => 'Propagating rate' },
{ name => 'direction_mask', type => '0 <= int32 <= 15', dead => 1,
desc => 'Direction mask' },
{ name => 'lower_limit', type => '0 <= int32 <= 255', dead => 1,
desc => 'Lower limit' },
{ name => 'upper_limit', type => '0 <= int32 <= 255', dead => 1,
desc => 'Upper limit' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:value-propagate",
"mode", 0, /* GEGL_VALUE_PROPAGATE_MODE_WHITE */
"lower-threshold", 0.0,
"upper-threshold", 1.0,
"rate", 1.0,
"top", TRUE,
"left", TRUE,
"right", TRUE,
"bottom", TRUE,
"value", TRUE,
"alpha", FALSE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Dilate"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_erode {
$blurb = 'Shrink lighter areas of the image',
$help = <<'HELP';
Erode image.
HELP
&std_pdb_compat('gegl:value-propagate');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'propagate_mode', type => '0 <= int32 <= 7', dead => 1,
desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 5:background, 6:opaque, 7:transparent }' },
{ name => 'propagating_channel', type => 'int32', dead => 1,
desc => 'Channels which values are propagated' },
{ name => 'propagating_rate', type => '0.0 <= float <= 1.0', dead => 1,
desc => 'Propagating rate' },
{ name => 'direction_mask', type => '0 <= int32 <= 15', dead => 1,
desc => 'Direction mask' },
{ name => 'lower_limit', type => '0 <= int32 <= 255', dead => 1,
desc => 'Lower limit' },
{ name => 'upper_limit', type => '0 <= int32 <= 255', dead => 1,
desc => 'Upper limit' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:value-propagate",
"mode", 1, /* GEGL_VALUE_PROPAGATE_MODE_BLACK */
"lower-threshold", 0.0,
"upper-threshold", 1.0,
"rate", 1.0,
"top", TRUE,
"left", TRUE,
"right", TRUE,
"bottom", TRUE,
"value", TRUE,
"alpha", FALSE,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Erode"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_waves {
$blurb = 'Distort the image with waves';
$help = <<'HELP';
Distort the image with waves.
HELP
&std_pdb_compat('gegl:waves');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'amplitude', type => '0 <= float <= 101',
desc => 'The Amplitude of the Waves' },
{ name => 'phase', type => '-360 <= float <= 360',
desc => 'The Phase of the Waves' },
{ name => 'wavelength', type => '0.1 <= float <= 50',
desc => 'The Wavelength of the Waves' },
{ name => 'type', type => 'boolean',
desc => 'Type of waves: { 0 = smeared, 1 = black }' },
{ name => 'reflective', type => 'boolean', dead => 1,
desc => 'Use Reflection (not implemented)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
gdouble width = pika_item_get_width (PIKA_ITEM (drawable));
gdouble height = pika_item_get_height (PIKA_ITEM (drawable));
gdouble aspect;
while (phase < 0)
phase += 360.0;
phase = fmod (phase, 360.0);
aspect = CLAMP (width / height, 0.1, 10.0);
node = gegl_node_new_child (NULL,
"operation", "gegl:waves",
"x", 0.5,
"y", 0.5,
"amplitude", amplitude,
"phi", (phase - 180.0) / 180.0,
"period", wavelength * 2.0,
"aspect", aspect,
"clamp", ! type,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Waves"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_whirl_pinch {
$blurb = 'Distort an image by whirling and pinching';
$help = <<'HELP';
Distorts the image by whirling and pinching, which are two common
center-based, circular distortions. Whirling is like projecting the
image onto the surface of water in a toilet and flushing. Pinching is
similar to projecting the image onto an elastic surface and pressing
or pulling on the center of the surface.
HELP
&std_pdb_compat('gegl:whirl-pinch');
$date = '2013';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'whirl', type => '-720 <= float <= 720',
desc => 'Whirl angle (degrees)' },
{ name => 'pinch', type => '-1 <= float <= 1',
desc => 'Pinch amount' },
{ name => 'radius', type => '0 <= float <= 2',
desc => 'Radius (1.0 is the largest circle that fits in the image, and 2.0 goes all the way to the corners)' }
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:whirl-pinch",
"whirl", whirl,
"pinch", pinch,
"radius", radius,
NULL);
node = wrap_in_selection_bounds (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Whirl and Pinch"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_wind {
$blurb = 'Smear image to give windblown effect';
$help = <<'HELP';
Renders a wind effect.
HELP
&std_pdb_compat('gegl:wind');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum PikaRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'threshold', type => '0 <= int32 <= 50',
desc => 'Controls where blending will be done' },
{ name => 'direction', type => '0 <= int32 <= 3',
desc => 'Wind direction { 0:left, 1:right, 2:top, 3:bottom }' },
{ name => 'strength', type => '1 <= int32 <= 100',
desc => 'Controls the extent of the blending' },
{ name => 'algorithm', type => '0 <= int32 <= 1',
desc => 'Algorithm { WIND (0), BLAST (1) }' },
{ name => 'edge', type => '0 <= int32 <= 2',
desc => 'Affected edge { BOTH (0), LEADING (1), TRAILING (2) }' },
);
%invoke = (
code => <<'CODE'
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node =
gegl_node_new_child (NULL,
"operation", "gegl:wind",
"threshold", threshold,
"direction", direction,
"strength", strength,
"style", algorithm,
"edge", edge,
NULL);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Wind"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
$extra{app}->{code} = <<'CODE';
static GeglNode *
wrap_in_graph (GeglNode *node)
{
GeglNode *new_node;
GeglNode *input;
GeglNode *output;
new_node = gegl_node_new ();
gegl_node_add_child (new_node, node);
g_object_unref (node);
pika_gegl_node_set_underlying_operation (new_node, node);
input = gegl_node_get_input_proxy (new_node, "input");
output = gegl_node_get_output_proxy (new_node, "output");
gegl_node_link_many (input,
node,
output,
NULL);
return new_node;
}
static GeglNode *
wrap_in_selection_bounds (GeglNode *node,
PikaDrawable *drawable)
{
gint x, y;
gint width, height;
if (pika_item_mask_intersect (PIKA_ITEM (drawable),
&x, &y, &width, &height))
{
GeglNode *new_node;
GeglNode *input;
GeglNode *output;
GeglNode *translate_before;
GeglNode *crop;
GeglNode *translate_after;
new_node = gegl_node_new ();
gegl_node_add_child (new_node, node);
g_object_unref (node);
pika_gegl_node_set_underlying_operation (new_node, node);
input = gegl_node_get_input_proxy (new_node, "input");
output = gegl_node_get_output_proxy (new_node, "output");
translate_before = gegl_node_new_child (new_node,
"operation", "gegl:translate",
"x", (gdouble) -x,
"y", (gdouble) -y,
NULL);
crop = gegl_node_new_child (new_node,
"operation", "gegl:crop",
"width", (gdouble) width,
"height", (gdouble) height,
NULL);
translate_after = gegl_node_new_child (new_node,
"operation", "gegl:translate",
"x", (gdouble) x,
"y", (gdouble) y,
NULL);
gegl_node_link_many (input,
translate_before,
crop,
node,
translate_after,
output,
NULL);
return new_node;
}
else
{
return node;
}
}
static GeglNode *
wrap_in_gamma_cast (GeglNode *node,
PikaDrawable *drawable)
{
if (pika_drawable_get_trc (drawable) != PIKA_TRC_LINEAR)
{
const Babl *drawable_format;
const Babl *cast_format;
GeglNode *new_node;
GeglNode *input;
GeglNode *output;
GeglNode *cast_before;
GeglNode *cast_after;
drawable_format = pika_drawable_get_format (drawable);
cast_format =
pika_babl_format (pika_babl_format_get_base_type (drawable_format),
pika_babl_precision (pika_babl_format_get_component_type (drawable_format),
PIKA_TRC_LINEAR),
babl_format_has_alpha (drawable_format),
babl_format_get_space (drawable_format));
new_node = gegl_node_new ();
gegl_node_add_child (new_node, node);
g_object_unref (node);
pika_gegl_node_set_underlying_operation (new_node, node);
input = gegl_node_get_input_proxy (new_node, "input");
output = gegl_node_get_output_proxy (new_node, "output");
cast_before = gegl_node_new_child (new_node,
"operation", "gegl:cast-format",
"input-format", drawable_format,
"output-format", cast_format,
NULL);
cast_after = gegl_node_new_child (new_node,
"operation", "gegl:cast-format",
"input-format", cast_format,
"output-format", drawable_format,
NULL);
gegl_node_link_many (input,
cast_before,
node,
cast_after,
output,
NULL);
return new_node;
}
else
{
return node;
}
}
static GeglNode *
create_buffer_source_node (GeglNode *parent,
PikaDrawable *drawable)
{
GeglNode *new_node;
GeglBuffer *buffer;
buffer = pika_drawable_get_buffer (drawable);
g_object_ref (buffer);
new_node = gegl_node_new_child (parent,
"operation", "gegl:buffer-source",
"buffer", buffer,
NULL);
g_object_unref (buffer);
return new_node;
}
static gboolean
bump_map (PikaDrawable *drawable,
PikaDrawable *bump_map,
gdouble azimuth,
gdouble elevation,
gint depth,
gint offset_x,
gint offset_y,
gdouble waterlevel,
gdouble ambient,
gboolean compensate,
gboolean invert,
gint type,
gboolean tiled,
PikaProgress *progress,
GError **error)
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *graph;
GeglNode *node;
GeglNode *src_node;
node = gegl_node_new_child (NULL,
"operation", "gegl:bump-map",
"tiled", tiled,
"type", type,
"compensate", compensate,
"invert", invert,
"azimuth", azimuth,
"elevation", elevation,
"depth", depth,
"offset_x", offset_x,
"offset_y", offset_y,
"waterlevel", waterlevel,
"ambient", ambient,
NULL);
graph = wrap_in_graph (node);
src_node = create_buffer_source_node (graph, bump_map);
gegl_node_connect (src_node, "output", node, "aux");
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Bump Map"),
graph);
g_object_unref (graph);
return TRUE;
}
else
return FALSE;
}
static gboolean
displace (PikaDrawable *drawable,
gdouble amount_x,
gdouble amount_y,
gboolean do_x,
gboolean do_y,
PikaDrawable *displace_map_x,
PikaDrawable *displace_map_y,
gint displace_type,
gint displace_mode,
PikaProgress *progress,
GError **error)
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
if (do_x || do_y)
{
GeglNode *graph;
GeglNode *node;
GeglAbyssPolicy abyss_policy = GEGL_ABYSS_NONE;
switch (displace_type)
{
case 1:
abyss_policy = GEGL_ABYSS_LOOP;
break;
case 2:
abyss_policy = GEGL_ABYSS_CLAMP;
break;
case 3:
abyss_policy = GEGL_ABYSS_BLACK;
break;
}
node = gegl_node_new_child (NULL,
"operation", "gegl:displace",
"displace_mode", displace_mode,
"sampler_type", GEGL_SAMPLER_CUBIC,
"abyss_policy", abyss_policy,
"amount_x", amount_x,
"amount_y", amount_y,
NULL);
graph = wrap_in_graph (node);
if (do_x)
{
GeglNode *src_node;
src_node = create_buffer_source_node (graph, displace_map_x);
gegl_node_connect (src_node, "output", node, "aux");
}
if (do_y)
{
GeglNode *src_node;
src_node = create_buffer_source_node (graph, displace_map_y);
gegl_node_connect (src_node, "output", node, "aux2");
}
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Displace"),
graph);
g_object_unref (graph);
}
return TRUE;
}
else
return FALSE;
}
static gboolean
gaussian_blur (PikaDrawable *drawable,
gdouble horizontal,
gdouble vertical,
PikaProgress *progress,
GError **error)
{
if (pika_pdb_item_is_attached (PIKA_ITEM (drawable), NULL,
PIKA_PDB_ITEM_CONTENT, error) &&
pika_pdb_item_is_not_group (PIKA_ITEM (drawable), error))
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation", "gegl:gaussian-blur",
"std-dev-x", horizontal * 0.32,
"std-dev-y", vertical * 0.32,
"abyss-policy", 1,
NULL);
node = wrap_in_gamma_cast (node, drawable);
pika_drawable_apply_operation (drawable, progress,
C_("undo-type", "Gaussian Blur"),
node);
g_object_unref (node);
return TRUE;
}
return FALSE;
}
static gint
newsprint_color_model (gint colorspace)
{
switch (colorspace)
{
case 0: return 1; /* black on white */
case 1: return 2; /* rgb */
case 2: return 3; /* cmyk */
case 3: return 1; /* black on white */
}
return 2;
}
static gint
newsprint_pattern (gint spotfn)
{
switch (spotfn)
{
case 0: return 1; /* circle */
case 1: return 0; /* line */
case 2: return 2; /* diamond */
case 3: return 4; /* ps circle */
case 4: return 2; /* FIXME postscript diamond */
}
return 1;
}
static gdouble
newsprint_angle (gdouble angle)
{
while (angle > 180.0)
angle -= 360.0;
while (angle < -180.0)
angle += 360.0;
return angle;
}
CODE
@headers = qw("libpikabase/pikabase.h"
"libpikaconfig/pikaconfig.h"
"libpikamath/pikamath.h"
"gegl/pika-babl.h"
"gegl/pika-gegl-utils.h"
"config/pikacoreconfig.h"
"core/pika.h"
"core/pikachannel.h"
"core/pikacontext.h"
"core/pikadrawable-operation.h"
"core/pikaimage-crop.h"
"core/pikaimage-resize.h"
"core/pikaimage-rotate.h"
"core/pikaimage-undo.h"
"core/pikapickable.h"
"core/pikapickable-auto-shrink.h"
"pikapdberror.h"
"pikapdb-utils.h"
"pika-intl.h");
@procs = qw(plug_in_alienmap2
plug_in_antialias
plug_in_apply_canvas
plug_in_applylens
plug_in_autocrop
plug_in_autocrop_layer
plug_in_autostretch_hsv
plug_in_bump_map
plug_in_bump_map_tiled
plug_in_c_astretch
plug_in_cartoon
plug_in_colors_channel_mixer
plug_in_colortoalpha
plug_in_convmatrix
plug_in_cubism
plug_in_deinterlace
plug_in_diffraction
plug_in_displace
plug_in_displace_polar
plug_in_dog
plug_in_edge
plug_in_emboss
plug_in_engrave
plug_in_exchange
plug_in_flarefx
plug_in_fractal_trace
plug_in_gauss
plug_in_gauss_iir
plug_in_gauss_iir2
plug_in_gauss_rle
plug_in_gauss_rle2
plug_in_glasstile
plug_in_hsv_noise
plug_in_illusion
plug_in_laplace
plug_in_lens_distortion
plug_in_make_seamless
plug_in_maze
plug_in_mblur
plug_in_mblur_inward
plug_in_median_blur
plug_in_mosaic
plug_in_neon
plug_in_newsprint
plug_in_normalize
plug_in_nova
plug_in_oilify
plug_in_oilify_enhanced
plug_in_papertile
plug_in_photocopy
plug_in_pixelize
plug_in_pixelize2
plug_in_plasma
plug_in_polar_coords
plug_in_red_eye_removal
plug_in_randomize_hurl
plug_in_randomize_pick
plug_in_randomize_slur
plug_in_rgb_noise
plug_in_ripple
plug_in_rotate
plug_in_noisify
plug_in_sel_gauss
plug_in_semiflatten
plug_in_shift
plug_in_sinus
plug_in_sobel
plug_in_softglow
plug_in_solid_noise
plug_in_spread
plug_in_threshold_alpha
plug_in_unsharp_mask
plug_in_video
plug_in_vinvert
plug_in_vpropagate
plug_in_dilate
plug_in_erode
plug_in_waves
plug_in_whirl_pinch
plug_in_wind);
%exports = (app => [@procs], lib => []);
$desc = 'Plug-in Compat';
$doc_title = 'pikaplugincompat';
$doc_short_desc = 'Compatibility for removed plug-ins.';
$doc_long_desc = 'Functions that perform the operation of removed plug-ins using GEGL operations or other PIKA internal functions.';
1;