553 lines
13 KiB
Plaintext
553 lines
13 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 selection_bounds {
|
||
|
$blurb = 'Find the bounding box of the current selection.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure returns whether there is a selection for the specified image. If
|
||
|
there is one, the upper left and lower right corners of the bounding box are
|
||
|
returned. These coordinates are relative to the image. Please note that the
|
||
|
pixel specified by the lower right coordinate of the bounding box is not
|
||
|
part of the selection. The selection ends at the upper left corner of this
|
||
|
pixel. This means the width of the selection can be calculated as (x2 - x1),
|
||
|
its height as (y2 - y1).
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
@outargs = (
|
||
|
{ name => 'non_empty', type => 'boolean', void_ret => 1,
|
||
|
desc => 'TRUE if there is a selection' },
|
||
|
{ name => 'x1', type => 'int32',
|
||
|
desc => 'x coordinate of upper left corner of selection bounds' },
|
||
|
{ name => 'y1', type => 'int32',
|
||
|
desc => 'y coordinate of upper left corner of selection bounds' },
|
||
|
{ name => 'x2', type => 'int32',
|
||
|
desc => 'x coordinate of lower right corner of selection bounds' },
|
||
|
{ name => 'y2', type => 'int32',
|
||
|
desc => 'y coordinate of lower right corner of selection bounds' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
gint x, y, w, h;
|
||
|
|
||
|
non_empty = pika_item_bounds (PIKA_ITEM (pika_image_get_mask (image)),
|
||
|
&x, &y, &w, &h);
|
||
|
|
||
|
x1 = x;
|
||
|
y1 = y;
|
||
|
x2 = x + w;
|
||
|
y2 = y + h;
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_value {
|
||
|
$blurb = 'Find the value of the selection at the specified coordinates.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure returns the value of the selection at the specified coordinates.
|
||
|
If the coordinates lie out of bounds, 0 is returned.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'x', type => 'int32',
|
||
|
desc => 'x coordinate of value' },
|
||
|
{ name => 'y', type => 'int32',
|
||
|
desc => 'y coordinate of value' }
|
||
|
);
|
||
|
|
||
|
@outargs = (
|
||
|
{ name => 'value', type => '0 <= int32 <= 255',
|
||
|
desc => 'Value of the selection' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
gdouble val;
|
||
|
|
||
|
val= pika_pickable_get_opacity_at (PIKA_PICKABLE (pika_image_get_mask (image)),
|
||
|
x, y);
|
||
|
|
||
|
value = ROUND (CLAMP (val, 0.0, 1.0) * 255.0);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_is_empty {
|
||
|
$blurb = 'Determine whether the selection is empty.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure returns TRUE if the selection for the specified image is empty.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
@outargs = (
|
||
|
{ name => 'is_empty', type => 'boolean',
|
||
|
desc => 'Is the selection empty?' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
is_empty = pika_channel_is_empty (pika_image_get_mask (image));
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_translate {
|
||
|
$blurb = 'Translate the selection by the specified offsets.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure actually translates the selection for the specified image by the
|
||
|
specified offsets. Regions that are translated from beyond the bounds of the
|
||
|
image are set to empty. Valid regions of the selection which are translated
|
||
|
beyond the bounds of the image because of this call are lost.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'offx', type => 'int32',
|
||
|
desc => 'x offset for translation' },
|
||
|
{ name => 'offy', type => 'int32',
|
||
|
desc => 'y offset for translation' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_item_translate (PIKA_ITEM (pika_image_get_mask (image)),
|
||
|
offx, offy, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_float {
|
||
|
$blurb = <<'BLURB';
|
||
|
Float the selection from the specified drawable with initial offsets as
|
||
|
specified.
|
||
|
BLURB
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure determines the region of the specified drawable that lies
|
||
|
beneath the current selection. The region is then cut from the drawable and the
|
||
|
resulting data is made into a new layer which is instantiated as a floating
|
||
|
selection. The offsets allow initial positioning of the new floating selection.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
$lib_private = 1;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'drawables', type => 'itemarray',
|
||
|
desc => 'The drawables from which to float selection',
|
||
|
no_validate => 1,
|
||
|
array => { name => 'num_drawables',
|
||
|
type => '1 <= int32',
|
||
|
desc => "The number of drawables" } },
|
||
|
{ name => 'offx', type => 'int32',
|
||
|
desc => 'x offset for translation' },
|
||
|
{ name => 'offy', type => 'int32',
|
||
|
desc => 'y offset for translation' }
|
||
|
);
|
||
|
|
||
|
@outargs = (
|
||
|
{ name => 'layer', type => 'layer',
|
||
|
desc => 'The floated layer' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
PikaImage *image = NULL;
|
||
|
gint i;
|
||
|
|
||
|
if (num_drawables < 1)
|
||
|
{
|
||
|
success = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i = 0; i < num_drawables; i++)
|
||
|
{
|
||
|
if (! pika_pdb_item_is_attached (PIKA_ITEM (drawables[i]), NULL,
|
||
|
PIKA_PDB_ITEM_CONTENT, error) ||
|
||
|
pika_pdb_item_is_group (PIKA_ITEM (drawables[i]), error) ||
|
||
|
(image && image != pika_item_get_image (PIKA_ITEM (drawables[i]))))
|
||
|
{
|
||
|
success = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
image = pika_item_get_image (PIKA_ITEM (drawables[i]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (success)
|
||
|
{
|
||
|
GList *drawable_list = NULL;
|
||
|
|
||
|
for (i = 0; i < num_drawables; i++)
|
||
|
drawable_list = g_list_prepend (drawable_list, (gpointer) drawables[i]);
|
||
|
|
||
|
layer = pika_selection_float (PIKA_SELECTION (pika_image_get_mask (image)),
|
||
|
drawable_list, context, TRUE, offx, offy,
|
||
|
error);
|
||
|
g_list_free (drawable_list);
|
||
|
if (! layer)
|
||
|
success = FALSE;
|
||
|
}
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_invert {
|
||
|
$blurb = 'Invert the selection mask.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure inverts the selection mask. For every pixel in the selection
|
||
|
channel, its new value is calculated as (255 - old-value).
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_invert (pika_image_get_mask (image), TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_sharpen {
|
||
|
$blurb = 'Sharpen the selection mask.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure sharpens the selection mask. For every pixel in the selection
|
||
|
channel, if the value is > 127, the new pixel is assigned a value of 255.
|
||
|
This removes any "anti-aliasing" that might exist in the selection mask's
|
||
|
boundary.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_sharpen (pika_image_get_mask (image), TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_all {
|
||
|
$blurb = 'Select all of the image.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure sets the selection mask to completely encompass the image.
|
||
|
Every pixel in the selection channel is set to 255.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_all (pika_image_get_mask (image), TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_none {
|
||
|
$blurb = 'Deselect the entire image.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure deselects the entire image. Every pixel in the selection channel
|
||
|
is set to 0.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_clear (pika_image_get_mask (image), NULL, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_feather {
|
||
|
$blurb = "Feather the image's selection";
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure feathers the selection. Feathering is implemented
|
||
|
using a gaussian blur.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'radius', type => '0 <= float',
|
||
|
desc => 'Radius of feather (in pixels)' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
/* FIXME: "edge-lock" hardcoded to TRUE */
|
||
|
pika_channel_feather (pika_image_get_mask (image),
|
||
|
radius, radius, TRUE, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_border {
|
||
|
$blurb = "Border the image's selection";
|
||
|
|
||
|
$help .= <<'HELP';
|
||
|
This procedure borders the selection. Bordering creates a new
|
||
|
selection which is defined along the boundary of the previous
|
||
|
selection at every point within the specified radius.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'radius', type => '0 <= int32',
|
||
|
desc => 'Radius of border (in pixels)' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
/* FIXME: "style" and "edge-lock" hardcoded to SMOOTH and TRUE, respectively. */
|
||
|
pika_channel_border (pika_image_get_mask (image),
|
||
|
radius, radius,
|
||
|
PIKA_CHANNEL_BORDER_STYLE_SMOOTH,
|
||
|
TRUE, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_grow {
|
||
|
$blurb = "Grow the image's selection";
|
||
|
|
||
|
$help .= <<'HELP';
|
||
|
This procedure grows the selection. Growing involves expanding the
|
||
|
boundary in all directions by the specified pixel amount.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'steps', type => '0 <= int32',
|
||
|
desc => 'Steps of grow (in pixels)' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_grow (pika_image_get_mask (image),
|
||
|
steps, steps, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_shrink {
|
||
|
$blurb = "Shrink the image's selection";
|
||
|
|
||
|
$help .= <<'HELP';
|
||
|
This procedure shrinks the selection. Shrinking involves trimming the
|
||
|
existing selection boundary on all sides by the specified number of
|
||
|
pixels.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' },
|
||
|
{ name => 'steps', type => '0 <= int32',
|
||
|
desc => 'Steps of shrink (in pixels)' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_shrink (pika_image_get_mask (image),
|
||
|
steps, steps, FALSE, TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_flood {
|
||
|
$blurb = "Remove holes from the image's selection";
|
||
|
|
||
|
$help .= <<'HELP';
|
||
|
This procedure removes holes from the selection, that can come from
|
||
|
selecting a patchy area with the Fuzzy Select Tool.
|
||
|
In technical terms this procedure floods the selection.
|
||
|
See the Algorithms page in the developer wiki for details.
|
||
|
HELP
|
||
|
|
||
|
$author = 'Ell';
|
||
|
$copyright = 'Ell';
|
||
|
$date = '2016';
|
||
|
$since = '2.10';
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
pika_channel_flood (pika_image_get_mask (image), TRUE);
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub selection_save {
|
||
|
$blurb = 'Copy the selection mask to a new channel.';
|
||
|
|
||
|
$help = <<'HELP';
|
||
|
This procedure copies the selection mask and stores the content in a new
|
||
|
channel. The new channel is automatically inserted into the image's list of
|
||
|
channels.
|
||
|
HELP
|
||
|
|
||
|
&std_pdb_misc;
|
||
|
|
||
|
@inargs = (
|
||
|
{ name => 'image', type => 'image',
|
||
|
desc => 'The image' }
|
||
|
);
|
||
|
|
||
|
@outargs = (
|
||
|
{ name => 'channel', type => 'channel',
|
||
|
desc => 'The new channel' }
|
||
|
);
|
||
|
|
||
|
%invoke = (
|
||
|
headers => [qw("core/pikaselection.h") ],
|
||
|
code => <<'CODE'
|
||
|
{
|
||
|
channel = PIKA_CHANNEL (pika_item_duplicate (PIKA_ITEM (pika_image_get_mask (image)),
|
||
|
PIKA_TYPE_CHANNEL));
|
||
|
|
||
|
if (channel)
|
||
|
{
|
||
|
/* saved selections are not visible by default */
|
||
|
pika_item_set_visible (PIKA_ITEM (channel), FALSE, FALSE);
|
||
|
|
||
|
pika_image_add_channel (image, channel,
|
||
|
PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
||
|
}
|
||
|
else
|
||
|
success = FALSE;
|
||
|
}
|
||
|
CODE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@headers = qw("libpikamath/pikamath.h"
|
||
|
"core/pikapickable.h"
|
||
|
"pikapdb-utils.h"
|
||
|
"pika-intl.h");
|
||
|
|
||
|
@procs = qw(selection_bounds selection_value selection_is_empty
|
||
|
selection_translate selection_float
|
||
|
selection_invert selection_sharpen selection_all selection_none
|
||
|
selection_feather selection_border selection_grow selection_shrink
|
||
|
selection_flood
|
||
|
selection_save);
|
||
|
|
||
|
%exports = (app => [@procs], lib => [@procs]);
|
||
|
|
||
|
$desc = 'Image mask';
|
||
|
$doc_title = 'pikaselection';
|
||
|
$doc_short_desc = 'Functions for manipulating selections.';
|
||
|
$doc_long_desc = 'Functions for manipulating selections.';
|
||
|
|
||
|
1;
|