2012-03-16 22:14:35 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* gimp-apply-operation.c
|
|
|
|
* Copyright (C) 2012 Øyvind Kolås <pippin@gimp.org>
|
|
|
|
* Sven Neumann <sven@gimp.org>
|
|
|
|
* Michael Natterer <mitch@gimp.org>
|
|
|
|
*
|
|
|
|
* 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
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2012-03-16 22:14:35 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2014-06-19 00:42:17 +08:00
|
|
|
#include <cairo.h>
|
2014-08-02 21:07:26 +08:00
|
|
|
#include <gio/gio.h>
|
2012-03-16 22:14:35 +08:00
|
|
|
#include <gegl.h>
|
|
|
|
|
2012-10-22 22:03:40 +08:00
|
|
|
#include "gimp-gegl-types.h"
|
2012-03-16 22:14:35 +08:00
|
|
|
|
2018-01-29 04:53:01 +08:00
|
|
|
#include "core/gimp-transform-utils.h"
|
2012-10-22 22:03:40 +08:00
|
|
|
#include "core/gimp-utils.h"
|
2019-01-12 16:41:00 +08:00
|
|
|
#include "core/gimpchunkiterator.h"
|
2012-10-22 22:03:40 +08:00
|
|
|
#include "core/gimpprogress.h"
|
|
|
|
|
|
|
|
#include "gimp-gegl-apply-operation.h"
|
2018-05-24 22:21:21 +08:00
|
|
|
#include "gimp-gegl-loops.h"
|
2012-11-01 06:47:45 +08:00
|
|
|
#include "gimp-gegl-nodes.h"
|
2018-05-24 22:21:21 +08:00
|
|
|
#include "gimp-gegl-utils.h"
|
2012-03-16 22:14:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-10-22 22:03:40 +08:00
|
|
|
gimp_gegl_apply_operation (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglNode *operation,
|
|
|
|
GeglBuffer *dest_buffer,
|
2018-03-23 01:31:00 +08:00
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gboolean crop_input)
|
2014-06-19 00:42:17 +08:00
|
|
|
{
|
|
|
|
gimp_gegl_apply_cached_operation (src_buffer,
|
|
|
|
progress, undo_desc,
|
|
|
|
operation,
|
|
|
|
dest_buffer,
|
|
|
|
dest_rect,
|
2018-03-23 01:31:00 +08:00
|
|
|
crop_input,
|
2014-06-30 06:10:25 +08:00
|
|
|
NULL, NULL, 0,
|
|
|
|
FALSE);
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
|
|
|
|
2014-06-30 06:10:25 +08:00
|
|
|
static void
|
|
|
|
gimp_gegl_apply_operation_cancel (GimpProgress *progress,
|
|
|
|
gboolean *cancel)
|
|
|
|
{
|
|
|
|
*cancel = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2014-06-19 00:42:17 +08:00
|
|
|
gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglNode *operation,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
2018-03-23 01:31:00 +08:00
|
|
|
gboolean crop_input,
|
2014-06-19 00:42:17 +08:00
|
|
|
GeglBuffer *cache,
|
|
|
|
const GeglRectangle *valid_rects,
|
2014-06-30 06:10:25 +08:00
|
|
|
gint n_valid_rects,
|
2018-07-01 22:19:54 +08:00
|
|
|
gboolean cancelable)
|
2012-03-16 22:14:35 +08:00
|
|
|
{
|
2019-01-12 16:41:00 +08:00
|
|
|
GeglNode *gegl;
|
|
|
|
GeglNode *effect;
|
|
|
|
GeglNode *dest_node;
|
|
|
|
GeglNode *operation_src_node = NULL;
|
|
|
|
GimpChunkIterator *iter;
|
|
|
|
cairo_region_t *region;
|
|
|
|
GeglRectangle rect = { 0, };
|
|
|
|
gboolean progress_started = FALSE;
|
|
|
|
gboolean cancel = FALSE;
|
|
|
|
gint all_pixels;
|
|
|
|
gint done_pixels;
|
2012-03-16 22:14:35 +08:00
|
|
|
|
2014-06-30 10:35:34 +08:00
|
|
|
g_return_val_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer), FALSE);
|
|
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
|
|
|
|
g_return_val_if_fail (GEGL_IS_NODE (operation), FALSE);
|
|
|
|
g_return_val_if_fail (GEGL_IS_BUFFER (dest_buffer), FALSE);
|
|
|
|
g_return_val_if_fail (cache == NULL || GEGL_IS_BUFFER (cache), FALSE);
|
|
|
|
g_return_val_if_fail (valid_rects == NULL || cache != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (valid_rects == NULL || n_valid_rects != 0, FALSE);
|
2012-03-16 22:14:35 +08:00
|
|
|
|
|
|
|
if (dest_rect)
|
|
|
|
{
|
|
|
|
rect = *dest_rect;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-02 21:19:47 +08:00
|
|
|
rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (dest_buffer),
|
2012-03-21 10:57:53 +08:00
|
|
|
gegl_buffer_get_height (dest_buffer));
|
2012-03-16 22:14:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gegl = gegl_node_new ();
|
|
|
|
|
2013-04-22 16:55:09 +08:00
|
|
|
if (! gegl_node_get_parent (operation))
|
|
|
|
gegl_node_add_child (gegl, operation);
|
|
|
|
|
2018-05-07 20:26:26 +08:00
|
|
|
effect = operation;
|
|
|
|
|
|
|
|
if (src_buffer)
|
2013-04-22 16:55:09 +08:00
|
|
|
{
|
|
|
|
GeglNode *src_node;
|
|
|
|
|
2013-06-18 14:06:24 +08:00
|
|
|
/* dup() because reading and writing the same buffer doesn't
|
2019-02-16 01:28:30 +08:00
|
|
|
* generally work with non-point ops when working in chunks.
|
|
|
|
* See bug #701875.
|
2013-06-18 14:06:24 +08:00
|
|
|
*/
|
2019-02-16 01:28:30 +08:00
|
|
|
if (src_buffer == dest_buffer &&
|
|
|
|
! gimp_gegl_node_is_point_operation (operation))
|
|
|
|
{
|
|
|
|
src_buffer = gegl_buffer_dup (src_buffer);
|
|
|
|
}
|
2013-06-18 14:06:24 +08:00
|
|
|
else
|
2019-02-16 01:28:30 +08:00
|
|
|
{
|
|
|
|
g_object_ref (src_buffer);
|
|
|
|
}
|
2013-06-18 14:06:24 +08:00
|
|
|
|
2013-04-22 16:55:09 +08:00
|
|
|
src_node = gegl_node_new_child (gegl,
|
|
|
|
"operation", "gegl:buffer-source",
|
|
|
|
"buffer", src_buffer,
|
|
|
|
NULL);
|
|
|
|
|
2013-06-18 14:06:24 +08:00
|
|
|
g_object_unref (src_buffer);
|
|
|
|
|
2018-03-23 01:31:00 +08:00
|
|
|
if (crop_input)
|
|
|
|
{
|
|
|
|
GeglNode *crop_node;
|
|
|
|
|
|
|
|
crop_node = gegl_node_new_child (gegl,
|
|
|
|
"operation", "gegl:crop",
|
|
|
|
"x", (gdouble) rect.x,
|
|
|
|
"y", (gdouble) rect.y,
|
|
|
|
"width", (gdouble) rect.width,
|
|
|
|
"height", (gdouble) rect.height,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gegl_node_connect_to (src_node, "output",
|
|
|
|
crop_node, "input");
|
|
|
|
|
|
|
|
src_node = crop_node;
|
|
|
|
}
|
|
|
|
|
2017-12-06 02:34:43 +08:00
|
|
|
operation_src_node = gegl_node_get_producer (operation, "input", NULL);
|
|
|
|
|
2018-05-07 20:26:26 +08:00
|
|
|
if (! gegl_node_has_pad (operation, "input"))
|
|
|
|
{
|
|
|
|
effect = gegl_node_new_child (gegl,
|
2018-05-08 02:36:29 +08:00
|
|
|
"operation", "gimp:normal",
|
2018-05-07 20:26:26 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
gegl_node_connect_to (operation, "output",
|
|
|
|
effect, "aux");
|
|
|
|
}
|
|
|
|
|
|
|
|
gegl_node_connect_to (src_node, "output",
|
|
|
|
effect, "input");
|
2013-04-22 16:55:09 +08:00
|
|
|
}
|
|
|
|
|
2012-03-16 22:14:35 +08:00
|
|
|
dest_node = gegl_node_new_child (gegl,
|
|
|
|
"operation", "gegl:write-buffer",
|
|
|
|
"buffer", dest_buffer,
|
|
|
|
NULL);
|
|
|
|
|
2018-05-07 20:26:26 +08:00
|
|
|
gegl_node_connect_to (effect, "output",
|
2013-04-22 16:55:09 +08:00
|
|
|
dest_node, "input");
|
2012-03-16 22:14:35 +08:00
|
|
|
|
|
|
|
if (progress)
|
2012-03-20 06:57:50 +08:00
|
|
|
{
|
2014-06-30 06:10:25 +08:00
|
|
|
if (gimp_progress_is_active (progress))
|
2012-03-20 06:57:50 +08:00
|
|
|
{
|
|
|
|
if (undo_desc)
|
2014-07-21 05:32:19 +08:00
|
|
|
gimp_progress_set_text_literal (progress, undo_desc);
|
2014-06-30 06:10:25 +08:00
|
|
|
|
|
|
|
progress_started = FALSE;
|
2018-07-01 22:19:54 +08:00
|
|
|
cancelable = FALSE;
|
2012-03-20 06:57:50 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-07-01 22:19:54 +08:00
|
|
|
gimp_progress_start (progress, cancelable, "%s", undo_desc);
|
2014-06-30 06:10:25 +08:00
|
|
|
|
2018-07-01 22:19:54 +08:00
|
|
|
if (cancelable)
|
2014-06-30 06:10:25 +08:00
|
|
|
g_signal_connect (progress, "cancel",
|
|
|
|
G_CALLBACK (gimp_gegl_apply_operation_cancel),
|
|
|
|
&cancel);
|
|
|
|
|
|
|
|
progress_started = TRUE;
|
2012-03-20 06:57:50 +08:00
|
|
|
}
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
2019-01-12 16:41:00 +08:00
|
|
|
else
|
2014-06-19 00:42:17 +08:00
|
|
|
{
|
2019-01-12 16:41:00 +08:00
|
|
|
cancelable = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gegl_buffer_freeze_changed (dest_buffer);
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
all_pixels = rect.width * rect.height;
|
|
|
|
done_pixels = 0;
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);
|
|
|
|
|
|
|
|
if (cache)
|
|
|
|
{
|
|
|
|
gint i;
|
2014-06-19 00:42:17 +08:00
|
|
|
|
|
|
|
for (i = 0; i < n_valid_rects; i++)
|
|
|
|
{
|
2018-12-30 20:47:47 +08:00
|
|
|
GeglRectangle valid_rect;
|
|
|
|
|
|
|
|
if (! gegl_rectangle_intersect (&valid_rect,
|
|
|
|
&valid_rects[i], &rect))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_gegl_buffer_copy (cache, &valid_rect, GEGL_ABYSS_NONE,
|
|
|
|
dest_buffer, &valid_rect);
|
2014-06-19 00:42:17 +08:00
|
|
|
|
|
|
|
cairo_region_subtract_rectangle (region,
|
|
|
|
(cairo_rectangle_int_t *)
|
2018-12-30 20:47:47 +08:00
|
|
|
&valid_rect);
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2018-12-30 20:47:47 +08:00
|
|
|
done_pixels += valid_rect.width * valid_rect.height;
|
2014-06-19 00:42:17 +08:00
|
|
|
|
|
|
|
if (progress)
|
2019-01-12 16:41:00 +08:00
|
|
|
{
|
|
|
|
gimp_progress_set_value (progress,
|
|
|
|
(gdouble) done_pixels /
|
|
|
|
(gdouble) all_pixels);
|
|
|
|
}
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
2019-01-12 16:41:00 +08:00
|
|
|
}
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
iter = gimp_chunk_iterator_new (region);
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
while (gimp_chunk_iterator_next (iter))
|
|
|
|
{
|
|
|
|
GeglRectangle render_rect;
|
2012-03-16 22:14:35 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
if (progress)
|
|
|
|
{
|
|
|
|
while (! cancel && g_main_context_pending (NULL))
|
|
|
|
g_main_context_iteration (NULL, FALSE);
|
2012-03-16 22:14:35 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
if (cancel)
|
|
|
|
break;
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
while (gimp_chunk_iterator_get_rect (iter, &render_rect))
|
2014-06-19 00:42:17 +08:00
|
|
|
{
|
2019-01-12 16:41:00 +08:00
|
|
|
gint rect_pixels = render_rect.width * render_rect.height;
|
2014-06-30 06:10:25 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
gegl_node_blit (dest_node, 1.0, &render_rect, NULL, NULL, 0,
|
|
|
|
GEGL_BLIT_DEFAULT);
|
|
|
|
|
|
|
|
done_pixels += rect_pixels;
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
2019-01-12 16:41:00 +08:00
|
|
|
|
|
|
|
if (progress)
|
2014-06-19 00:42:17 +08:00
|
|
|
{
|
2019-01-12 16:41:00 +08:00
|
|
|
gimp_progress_set_value (progress,
|
|
|
|
(gdouble) done_pixels /
|
|
|
|
(gdouble) all_pixels);
|
2014-06-19 00:42:17 +08:00
|
|
|
}
|
2013-03-03 01:30:22 +08:00
|
|
|
}
|
2012-03-16 22:14:35 +08:00
|
|
|
|
2019-01-12 16:41:00 +08:00
|
|
|
gegl_buffer_thaw_changed (dest_buffer);
|
2014-06-19 00:42:17 +08:00
|
|
|
|
2012-03-16 22:14:35 +08:00
|
|
|
g_object_unref (gegl);
|
|
|
|
|
2017-12-06 02:34:43 +08:00
|
|
|
if (operation_src_node)
|
|
|
|
{
|
|
|
|
gegl_node_connect_to (operation_src_node, "output",
|
|
|
|
operation, "input");
|
|
|
|
}
|
|
|
|
|
2014-06-30 06:10:25 +08:00
|
|
|
if (progress_started)
|
|
|
|
{
|
|
|
|
gimp_progress_end (progress);
|
|
|
|
|
2018-07-01 22:19:54 +08:00
|
|
|
if (cancelable)
|
2014-06-30 06:10:25 +08:00
|
|
|
g_signal_handlers_disconnect_by_func (progress,
|
|
|
|
gimp_gegl_apply_operation_cancel,
|
|
|
|
&cancel);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ! cancel;
|
2012-03-16 22:14:35 +08:00
|
|
|
}
|
2012-11-01 06:47:45 +08:00
|
|
|
|
|
|
|
void
|
2016-12-25 02:39:32 +08:00
|
|
|
gimp_gegl_apply_dither (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
gint levels,
|
|
|
|
gint dither_type)
|
2012-11-01 06:47:45 +08:00
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
2016-12-25 02:39:32 +08:00
|
|
|
levels = CLAMP (levels, 2, 65536);
|
2016-11-09 19:33:13 +08:00
|
|
|
|
2012-11-01 06:47:45 +08:00
|
|
|
node = gegl_node_new_child (NULL,
|
2016-12-25 02:39:32 +08:00
|
|
|
"operation", "gegl:dither",
|
|
|
|
"red-levels", levels,
|
|
|
|
"green-levels", levels,
|
|
|
|
"blue-levels", levels,
|
|
|
|
"alpha-bits", levels,
|
2016-09-28 18:05:41 +08:00
|
|
|
"dither-method", dither_type,
|
2012-11-01 06:47:45 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-10-21 23:21:10 +08:00
|
|
|
gimp_gegl_apply_flatten (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GimpRGB *background,
|
2018-08-10 02:04:44 +08:00
|
|
|
const Babl *space,
|
2017-10-21 23:21:10 +08:00
|
|
|
GimpLayerColorSpace composite_space)
|
2012-11-01 06:47:45 +08:00
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
g_return_if_fail (background != NULL);
|
|
|
|
|
2018-08-10 02:04:44 +08:00
|
|
|
node = gimp_gegl_create_flatten_node (background, space, composite_space);
|
2012-11-01 06:47:45 +08:00
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
2013-04-08 20:16:33 +08:00
|
|
|
void
|
2014-03-05 05:09:11 +08:00
|
|
|
gimp_gegl_apply_feather (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gdouble radius_x,
|
|
|
|
gdouble radius_y)
|
2013-04-08 20:16:33 +08:00
|
|
|
{
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
/* 3.5 is completely magic and picked to visually match the old
|
|
|
|
* gaussian_blur_region() on a crappy laptop display
|
|
|
|
*/
|
|
|
|
gimp_gegl_apply_gaussian_blur (src_buffer,
|
|
|
|
progress, undo_desc,
|
2014-03-05 05:09:11 +08:00
|
|
|
dest_buffer, dest_rect,
|
2013-04-08 20:16:33 +08:00
|
|
|
radius_x / 3.5,
|
|
|
|
radius_y / 3.5);
|
|
|
|
}
|
|
|
|
|
2012-11-01 06:47:45 +08:00
|
|
|
void
|
2016-04-04 23:11:05 +08:00
|
|
|
gimp_gegl_apply_border (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gint radius_x,
|
|
|
|
gint radius_y,
|
|
|
|
GimpChannelBorderStyle style,
|
|
|
|
gboolean edge_lock)
|
2014-03-05 05:09:11 +08:00
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
2016-04-04 23:11:05 +08:00
|
|
|
switch (style)
|
|
|
|
{
|
|
|
|
case GIMP_CHANNEL_BORDER_STYLE_HARD:
|
|
|
|
case GIMP_CHANNEL_BORDER_STYLE_FEATHERED:
|
|
|
|
{
|
|
|
|
gboolean feather = style == GIMP_CHANNEL_BORDER_STYLE_FEATHERED;
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:border",
|
|
|
|
"radius-x", radius_x,
|
|
|
|
"radius-y", radius_y,
|
|
|
|
"feather", feather,
|
|
|
|
"edge-lock", edge_lock,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_CHANNEL_BORDER_STYLE_SMOOTH:
|
|
|
|
{
|
|
|
|
GeglNode *input, *output;
|
|
|
|
GeglNode *grow, *shrink, *subtract;
|
|
|
|
|
|
|
|
node = gegl_node_new ();
|
|
|
|
|
|
|
|
input = gegl_node_get_input_proxy (node, "input");
|
|
|
|
output = gegl_node_get_output_proxy (node, "output");
|
|
|
|
|
|
|
|
/* Duplicate special-case behavior of "gimp:border". */
|
|
|
|
if (radius_x == 1 && radius_y == 1)
|
|
|
|
{
|
|
|
|
grow = gegl_node_new_child (node,
|
|
|
|
"operation", "gegl:nop",
|
|
|
|
NULL);
|
|
|
|
shrink = gegl_node_new_child (node,
|
|
|
|
"operation", "gimp:shrink",
|
|
|
|
"radius-x", 1,
|
|
|
|
"radius-y", 1,
|
|
|
|
"edge-lock", edge_lock,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grow = gegl_node_new_child (node,
|
|
|
|
"operation", "gimp:grow",
|
|
|
|
"radius-x", radius_x,
|
|
|
|
"radius-y", radius_y,
|
|
|
|
NULL);
|
|
|
|
shrink = gegl_node_new_child (node,
|
|
|
|
"operation", "gimp:shrink",
|
|
|
|
"radius-x", radius_x + 1,
|
|
|
|
"radius-y", radius_y + 1,
|
|
|
|
"edge-lock", edge_lock,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
subtract = gegl_node_new_child (node,
|
|
|
|
"operation", "gegl:subtract",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gegl_node_link_many (input, grow, subtract, output, NULL);
|
|
|
|
gegl_node_link (input, shrink);
|
|
|
|
gegl_node_connect_to (shrink, "output", subtract, "aux");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-02-12 05:23:10 +08:00
|
|
|
gimp_assert_not_reached ();
|
2016-04-04 23:11:05 +08:00
|
|
|
}
|
2014-03-05 05:09:11 +08:00
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
app: crop input to output rect in channel ops
In gimp_gegl_apply_{border,grow,shrink,flood}(), which are used
by the corresponding channel functions, pass crop_input = TRUE to
gimp_gegl_apply_operation(), to clip the input to the output rect.
These operations process the entire input in one go, regardless of
the requested output region; however, the channel functions
calculate the output region according to the known channel bounds,
hence clipping the input to these bounds doesn't affect discard any
information, while avoiding unnecessary work. In particular, this
makes the corresponding operations on small selections in big images
much faster.
2018-03-23 01:38:11 +08:00
|
|
|
node, dest_buffer, dest_rect, TRUE);
|
2014-03-05 05:09:11 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_grow (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gint radius_x,
|
|
|
|
gint radius_y)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:grow",
|
|
|
|
"radius-x", radius_x,
|
|
|
|
"radius-y", radius_y,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
app: crop input to output rect in channel ops
In gimp_gegl_apply_{border,grow,shrink,flood}(), which are used
by the corresponding channel functions, pass crop_input = TRUE to
gimp_gegl_apply_operation(), to clip the input to the output rect.
These operations process the entire input in one go, regardless of
the requested output region; however, the channel functions
calculate the output region according to the known channel bounds,
hence clipping the input to these bounds doesn't affect discard any
information, while avoiding unnecessary work. In particular, this
makes the corresponding operations on small selections in big images
much faster.
2018-03-23 01:38:11 +08:00
|
|
|
node, dest_buffer, dest_rect, TRUE);
|
2014-03-05 05:09:11 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_shrink (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gint radius_x,
|
|
|
|
gint radius_y,
|
|
|
|
gboolean edge_lock)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:shrink",
|
|
|
|
"radius-x", radius_x,
|
|
|
|
"radius-y", radius_y,
|
|
|
|
"edge-lock", edge_lock,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
app: crop input to output rect in channel ops
In gimp_gegl_apply_{border,grow,shrink,flood}(), which are used
by the corresponding channel functions, pass crop_input = TRUE to
gimp_gegl_apply_operation(), to clip the input to the output rect.
These operations process the entire input in one go, regardless of
the requested output region; however, the channel functions
calculate the output region according to the known channel bounds,
hence clipping the input to these bounds doesn't affect discard any
information, while avoiding unnecessary work. In particular, this
makes the corresponding operations on small selections in big images
much faster.
2018-03-23 01:38:11 +08:00
|
|
|
node, dest_buffer, dest_rect, TRUE);
|
2014-03-05 05:09:11 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
2016-01-25 01:08:43 +08:00
|
|
|
void
|
|
|
|
gimp_gegl_apply_flood (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:flood",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
app: crop input to output rect in channel ops
In gimp_gegl_apply_{border,grow,shrink,flood}(), which are used
by the corresponding channel functions, pass crop_input = TRUE to
gimp_gegl_apply_operation(), to clip the input to the output rect.
These operations process the entire input in one go, regardless of
the requested output region; however, the channel functions
calculate the output region according to the known channel bounds,
hence clipping the input to these bounds doesn't affect discard any
information, while avoiding unnecessary work. In particular, this
makes the corresponding operations on small selections in big images
much faster.
2018-03-23 01:38:11 +08:00
|
|
|
node, dest_buffer, dest_rect, TRUE);
|
2016-01-25 01:08:43 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
2014-03-05 05:09:11 +08:00
|
|
|
void
|
|
|
|
gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
const GeglRectangle *dest_rect,
|
|
|
|
gdouble std_dev_x,
|
|
|
|
gdouble std_dev_y)
|
2012-11-01 06:47:45 +08:00
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gegl:gaussian-blur",
|
|
|
|
"std-dev-x", std_dev_x,
|
|
|
|
"std-dev-y", std_dev_y,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, dest_rect, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-06-24 06:45:04 +08:00
|
|
|
gimp_gegl_apply_invert_gamma (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer)
|
2012-11-01 06:47:45 +08:00
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
2013-06-24 06:45:04 +08:00
|
|
|
"operation", "gegl:invert-gamma",
|
2012-11-01 06:47:45 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
2013-06-24 06:45:04 +08:00
|
|
|
void
|
|
|
|
gimp_gegl_apply_invert_linear (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gegl:invert-linear",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2013-06-24 06:45:04 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
2012-11-01 06:47:45 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_opacity (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
GeglBuffer *mask,
|
|
|
|
gint mask_offset_x,
|
|
|
|
gint mask_offset_y,
|
|
|
|
gdouble opacity)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
g_return_if_fail (mask == NULL || GEGL_IS_BUFFER (mask));
|
|
|
|
|
|
|
|
node = gimp_gegl_create_apply_opacity_node (mask,
|
|
|
|
mask_offset_x,
|
|
|
|
mask_offset_y,
|
|
|
|
opacity);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_scale (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
GimpInterpolationType interpolation_type,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
2018-03-23 04:53:08 +08:00
|
|
|
"operation", "gegl:scale-ratio",
|
|
|
|
"origin-x", 0.0,
|
|
|
|
"origin-y", 0.0,
|
|
|
|
"sampler", interpolation_type,
|
|
|
|
"abyss-policy", GEGL_ABYSS_CLAMP,
|
|
|
|
"x", x,
|
|
|
|
"y", y,
|
2012-11-01 06:47:45 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_set_alpha (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
gdouble value)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gimp:set-alpha",
|
|
|
|
"value", value,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_threshold (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
gdouble value)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
|
|
|
"operation", "gegl:threshold",
|
|
|
|
"value", value,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_gegl_apply_transform (GeglBuffer *src_buffer,
|
|
|
|
GimpProgress *progress,
|
|
|
|
const gchar *undo_desc,
|
|
|
|
GeglBuffer *dest_buffer,
|
|
|
|
GimpInterpolationType interpolation_type,
|
|
|
|
GimpMatrix3 *transform)
|
|
|
|
{
|
|
|
|
GeglNode *node;
|
|
|
|
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
|
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
|
|
|
|
|
|
|
node = gegl_node_new_child (NULL,
|
2018-01-29 03:50:13 +08:00
|
|
|
"operation", "gegl:transform",
|
2018-01-29 04:53:01 +08:00
|
|
|
"near-z", GIMP_TRANSFORM_NEAR_Z,
|
2018-01-29 03:50:13 +08:00
|
|
|
"sampler", interpolation_type,
|
2012-11-01 06:47:45 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_gegl_node_set_matrix (node, transform);
|
|
|
|
|
|
|
|
gimp_gegl_apply_operation (src_buffer, progress, undo_desc,
|
2018-03-23 01:31:00 +08:00
|
|
|
node, dest_buffer, NULL, FALSE);
|
2012-11-01 06:47:45 +08:00
|
|
|
g_object_unref (node);
|
|
|
|
}
|