5605 lines
181 KiB
Plaintext
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;
|