Initial checkin of Pika from heckimp
This commit is contained in:
60
app/gegl/meson.build
Normal file
60
app/gegl/meson.build
Normal file
@ -0,0 +1,60 @@
|
||||
stamp_gegl_enums = custom_target('stamp-pika-gegl-enums.h',
|
||||
input : [
|
||||
files(
|
||||
'pika-gegl-enums.h'
|
||||
),
|
||||
],
|
||||
output: [ 'stamp-pika-gegl-enums.h', ],
|
||||
command: [
|
||||
mkenums_wrap, perl,
|
||||
meson.project_source_root(), meson.current_source_dir(),
|
||||
meson.current_build_dir(),
|
||||
'pika-gegl-',
|
||||
'#include <gio/gio.h>\n' +
|
||||
'#include "libpikabase/pikabase.h"\n' +
|
||||
'#include "core/core-enums.h"\n',
|
||||
'#include "pika-intl.h"'
|
||||
],
|
||||
build_by_default: true
|
||||
)
|
||||
|
||||
libappgegl_loops = simd.check('pika-gegl-loops-simd',
|
||||
sse2: 'pika-gegl-loops-sse2.c',
|
||||
compiler: cc,
|
||||
include_directories: [ rootInclude, rootAppInclude, ],
|
||||
dependencies: [
|
||||
cairo,
|
||||
gegl,
|
||||
gdk_pixbuf,
|
||||
],
|
||||
)
|
||||
|
||||
libappgegl_sources = [
|
||||
'pika-babl-compat.c',
|
||||
'pika-babl.c',
|
||||
'pika-gegl-apply-operation.c',
|
||||
'pika-gegl-loops.cc',
|
||||
'pika-gegl-mask-combine.cc',
|
||||
'pika-gegl-mask.c',
|
||||
'pika-gegl-nodes.c',
|
||||
'pika-gegl-tile-compat.c',
|
||||
'pika-gegl-utils.c',
|
||||
'pika-gegl.c',
|
||||
'pikaapplicator.c',
|
||||
'pikatilehandlervalidate.c',
|
||||
|
||||
'pika-gegl-enums.c',
|
||||
stamp_gegl_enums
|
||||
]
|
||||
|
||||
libappgegl = static_library('appgegl',
|
||||
libappgegl_sources,
|
||||
link_with: libappgegl_loops[0],
|
||||
include_directories: [ rootInclude, rootAppInclude, ],
|
||||
c_args: '-DG_LOG_DOMAIN="Pika-GEGL"',
|
||||
dependencies: [
|
||||
cairo,
|
||||
gegl,
|
||||
gdk_pixbuf,
|
||||
],
|
||||
)
|
105
app/gegl/pika-babl-compat.c
Normal file
105
app/gegl/pika-babl-compat.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-babl-compat.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "pika-babl.h"
|
||||
#include "pika-babl-compat.h"
|
||||
|
||||
|
||||
PikaImageType
|
||||
pika_babl_format_get_image_type (const Babl *format)
|
||||
{
|
||||
const gchar *name;
|
||||
|
||||
g_return_val_if_fail (format != NULL, -1);
|
||||
|
||||
name = babl_get_name (babl_format_get_model (format));
|
||||
|
||||
if (! strcmp (name, "Y") ||
|
||||
! strcmp (name, "Y'") ||
|
||||
! strcmp (name, "Y~"))
|
||||
{
|
||||
return PIKA_GRAY_IMAGE;
|
||||
}
|
||||
else if (! strcmp (name, "YA") ||
|
||||
! strcmp (name, "Y'A") ||
|
||||
! strcmp (name, "Y~A"))
|
||||
{
|
||||
return PIKA_GRAYA_IMAGE;
|
||||
}
|
||||
else if (! strcmp (name, "RGB") ||
|
||||
! strcmp (name, "R'G'B'") ||
|
||||
! strcmp (name, "R~G~B~"))
|
||||
{
|
||||
return PIKA_RGB_IMAGE;
|
||||
}
|
||||
else if (! strcmp (name, "RGBA") ||
|
||||
! strcmp (name, "R'G'B'A") ||
|
||||
! strcmp (name, "R~G~B~A"))
|
||||
{
|
||||
return PIKA_RGBA_IMAGE;
|
||||
}
|
||||
else if (babl_format_is_palette (format))
|
||||
{
|
||||
if (babl_format_has_alpha (format))
|
||||
return PIKA_INDEXEDA_IMAGE;
|
||||
else
|
||||
return PIKA_INDEXED_IMAGE;
|
||||
}
|
||||
|
||||
g_return_val_if_reached (-1);
|
||||
}
|
||||
|
||||
const Babl *
|
||||
pika_babl_compat_u8_format (const Babl *format)
|
||||
{
|
||||
g_return_val_if_fail (format != NULL, NULL);
|
||||
|
||||
/* indexed images only exist in u8, return the same format */
|
||||
if (babl_format_is_palette (format))
|
||||
return format;
|
||||
|
||||
return pika_babl_format (pika_babl_format_get_base_type (format),
|
||||
PIKA_PRECISION_U8_NON_LINEAR,
|
||||
babl_format_has_alpha (format),
|
||||
babl_format_get_space (format));
|
||||
}
|
||||
|
||||
const Babl *
|
||||
pika_babl_compat_u8_mask_format (const Babl *format)
|
||||
{
|
||||
g_return_val_if_fail (format != NULL, NULL);
|
||||
|
||||
return pika_babl_format (pika_babl_format_get_base_type (format),
|
||||
PIKA_PRECISION_U8_LINEAR,
|
||||
FALSE,
|
||||
NULL);
|
||||
}
|
35
app/gegl/pika-babl-compat.h
Normal file
35
app/gegl/pika-babl-compat.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-babl-compat.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_BABL_COMPAT_H__
|
||||
#define __PIKA_BABL_COMPAT_H__
|
||||
|
||||
|
||||
PikaImageType pika_babl_format_get_image_type (const Babl *format);
|
||||
|
||||
const Babl * pika_babl_compat_u8_format (const Babl *format);
|
||||
const Babl * pika_babl_compat_u8_mask_format (const Babl *format);
|
||||
|
||||
|
||||
#endif /* __PIKA_BABL_COMPAT_H__ */
|
1668
app/gegl/pika-babl.c
Normal file
1668
app/gegl/pika-babl.c
Normal file
File diff suppressed because it is too large
Load Diff
69
app/gegl/pika-babl.h
Normal file
69
app/gegl/pika-babl.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-babl.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_BABL_H__
|
||||
#define __PIKA_BABL_H__
|
||||
|
||||
|
||||
void pika_babl_init (void);
|
||||
void pika_babl_init_fishes (PikaInitStatusFunc status_callback);
|
||||
|
||||
const gchar * pika_babl_format_get_description (const Babl *format);
|
||||
PikaColorProfile * pika_babl_format_get_color_profile (const Babl *format);
|
||||
PikaColorProfile * pika_babl_get_builtin_color_profile (PikaImageBaseType base_type,
|
||||
PikaTRCType trc);
|
||||
|
||||
PikaImageBaseType pika_babl_format_get_base_type (const Babl *format);
|
||||
PikaComponentType pika_babl_format_get_component_type (const Babl *format);
|
||||
PikaPrecision pika_babl_format_get_precision (const Babl *format);
|
||||
PikaTRCType pika_babl_format_get_trc (const Babl *format);
|
||||
|
||||
PikaComponentType pika_babl_component_type (PikaPrecision precision);
|
||||
PikaTRCType pika_babl_trc (PikaPrecision precision);
|
||||
PikaPrecision pika_babl_precision (PikaComponentType component,
|
||||
PikaTRCType trc);
|
||||
|
||||
gboolean pika_babl_is_valid (PikaImageBaseType base_type,
|
||||
PikaPrecision precision);
|
||||
PikaComponentType pika_babl_is_bounded (PikaPrecision precision);
|
||||
|
||||
const Babl * pika_babl_format (PikaImageBaseType base_type,
|
||||
PikaPrecision precision,
|
||||
gboolean with_alpha,
|
||||
const Babl *space);
|
||||
const Babl * pika_babl_mask_format (PikaPrecision precision);
|
||||
const Babl * pika_babl_component_format (PikaImageBaseType base_type,
|
||||
PikaPrecision precision,
|
||||
gint index);
|
||||
|
||||
const Babl * pika_babl_format_change_component_type (const Babl *format,
|
||||
PikaComponentType component);
|
||||
const Babl * pika_babl_format_change_trc (const Babl *format,
|
||||
PikaTRCType trc);
|
||||
|
||||
gchar ** pika_babl_print_pixel (const Babl *format,
|
||||
gpointer pixel);
|
||||
|
||||
|
||||
#endif /* __PIKA_BABL_H__ */
|
827
app/gegl/pika-gegl-apply-operation.c
Normal file
827
app/gegl/pika-gegl-apply-operation.c
Normal file
@ -0,0 +1,827 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "core/pika-transform-utils.h"
|
||||
#include "core/pika-utils.h"
|
||||
#include "core/pikachunkiterator.h"
|
||||
#include "core/pikaprogress.h"
|
||||
|
||||
#include "pika-gegl-apply-operation.h"
|
||||
#include "pika-gegl-loops.h"
|
||||
#include "pika-gegl-nodes.h"
|
||||
#include "pika-gegl-utils.h"
|
||||
|
||||
|
||||
/* iteration interval when applying an operation interactively
|
||||
* (with progress indication)
|
||||
*/
|
||||
#define APPLY_OPERATION_INTERACTIVE_INTERVAL (1.0 / 8.0) /* seconds */
|
||||
|
||||
/* iteration interval when applying an operation non-interactively
|
||||
* (without progress indication)
|
||||
*/
|
||||
#define APPLY_OPERATION_NON_INTERACTIVE_INTERVAL 1.0 /* seconds */
|
||||
|
||||
|
||||
void
|
||||
pika_gegl_apply_operation (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglNode *operation,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gboolean crop_input)
|
||||
{
|
||||
pika_gegl_apply_cached_operation (src_buffer,
|
||||
progress, undo_desc,
|
||||
operation,
|
||||
src_buffer != NULL,
|
||||
dest_buffer,
|
||||
dest_rect,
|
||||
crop_input,
|
||||
NULL, NULL, 0,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_apply_operation_cancel (PikaProgress *progress,
|
||||
gboolean *cancel)
|
||||
{
|
||||
*cancel = TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_apply_cached_operation (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglNode *operation,
|
||||
gboolean connect_src_buffer,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gboolean crop_input,
|
||||
GeglBuffer *cache,
|
||||
const GeglRectangle *valid_rects,
|
||||
gint n_valid_rects,
|
||||
gboolean cancelable)
|
||||
{
|
||||
GeglNode *gegl;
|
||||
GeglNode *effect;
|
||||
GeglNode *dest_node;
|
||||
GeglNode *underlying_operation;
|
||||
GeglNode *operation_src_node = NULL;
|
||||
GeglBuffer *result_buffer;
|
||||
PikaChunkIterator *iter;
|
||||
cairo_region_t *region;
|
||||
gboolean progress_started = FALSE;
|
||||
gboolean cancel = FALSE;
|
||||
gint64 all_pixels;
|
||||
gint64 done_pixels;
|
||||
|
||||
g_return_val_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer), FALSE);
|
||||
g_return_val_if_fail (progress == NULL || PIKA_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);
|
||||
|
||||
if (! dest_rect)
|
||||
dest_rect = gegl_buffer_get_extent (dest_buffer);
|
||||
|
||||
if (progress)
|
||||
{
|
||||
if (pika_progress_is_active (progress))
|
||||
{
|
||||
if (undo_desc)
|
||||
pika_progress_set_text_literal (progress, undo_desc);
|
||||
|
||||
progress_started = FALSE;
|
||||
cancelable = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_progress_start (progress, cancelable, "%s", undo_desc);
|
||||
|
||||
if (cancelable)
|
||||
g_signal_connect (progress, "cancel",
|
||||
G_CALLBACK (pika_gegl_apply_operation_cancel),
|
||||
&cancel);
|
||||
|
||||
progress_started = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelable = FALSE;
|
||||
}
|
||||
|
||||
gegl_buffer_freeze_changed (dest_buffer);
|
||||
|
||||
underlying_operation = pika_gegl_node_get_underlying_operation (operation);
|
||||
|
||||
result_buffer = dest_buffer;
|
||||
|
||||
if (result_buffer == src_buffer &&
|
||||
! (pika_gegl_node_is_point_operation (underlying_operation) ||
|
||||
pika_gegl_node_is_source_operation (underlying_operation)))
|
||||
{
|
||||
/* Write the result to a temporary buffer, instead of directly to
|
||||
* dest_buffer, since reading and writing the same buffer doesn't
|
||||
* generally work with non-point ops when working in chunks.
|
||||
*
|
||||
* See bug #701875.
|
||||
*/
|
||||
|
||||
if (cache)
|
||||
{
|
||||
/* If we have a cache, use it directly as the temporary result
|
||||
* buffer, and skip copying the cached results to result_buffer
|
||||
* below. Instead, the cached results are copied together with the
|
||||
* newly rendered results in a single step at the end of processing.
|
||||
*/
|
||||
|
||||
g_warn_if_fail (cache != dest_buffer);
|
||||
|
||||
result_buffer = g_object_ref (cache);
|
||||
|
||||
cache = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_buffer = gegl_buffer_new (
|
||||
dest_rect, gegl_buffer_get_format (dest_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
all_pixels = (gint64) dest_rect->width * (gint64) dest_rect->height;
|
||||
done_pixels = 0;
|
||||
|
||||
region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) dest_rect);
|
||||
|
||||
if (n_valid_rects > 0)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n_valid_rects; i++)
|
||||
{
|
||||
GeglRectangle valid_rect;
|
||||
|
||||
if (! gegl_rectangle_intersect (&valid_rect,
|
||||
&valid_rects[i], dest_rect))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cache)
|
||||
{
|
||||
pika_gegl_buffer_copy (
|
||||
cache, &valid_rect, GEGL_ABYSS_NONE,
|
||||
result_buffer, &valid_rect);
|
||||
}
|
||||
|
||||
cairo_region_subtract_rectangle (region,
|
||||
(cairo_rectangle_int_t *)
|
||||
&valid_rect);
|
||||
|
||||
done_pixels += (gint64) valid_rect.width * (gint64) valid_rect.height;
|
||||
|
||||
if (progress)
|
||||
{
|
||||
pika_progress_set_value (progress,
|
||||
(gdouble) done_pixels /
|
||||
(gdouble) all_pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gegl = gegl_node_new ();
|
||||
|
||||
if (! gegl_node_get_parent (operation))
|
||||
gegl_node_add_child (gegl, operation);
|
||||
|
||||
effect = operation;
|
||||
|
||||
if (connect_src_buffer || crop_input)
|
||||
{
|
||||
GeglNode *src_node;
|
||||
|
||||
operation_src_node = gegl_node_get_producer (operation, "input", NULL);
|
||||
|
||||
src_node = operation_src_node;
|
||||
|
||||
if (connect_src_buffer)
|
||||
{
|
||||
src_node = gegl_node_new_child (gegl,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", src_buffer,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (crop_input)
|
||||
{
|
||||
GeglNode *crop_node;
|
||||
|
||||
crop_node = gegl_node_new_child (gegl,
|
||||
"operation", "gegl:crop",
|
||||
"x", (gdouble) dest_rect->x,
|
||||
"y", (gdouble) dest_rect->y,
|
||||
"width", (gdouble) dest_rect->width,
|
||||
"height", (gdouble) dest_rect->height,
|
||||
NULL);
|
||||
|
||||
gegl_node_link (src_node, crop_node);
|
||||
|
||||
src_node = crop_node;
|
||||
}
|
||||
|
||||
if (! gegl_node_has_pad (operation, "input"))
|
||||
{
|
||||
effect = gegl_node_new_child (gegl,
|
||||
"operation", "pika:normal",
|
||||
NULL);
|
||||
|
||||
gegl_node_connect (operation, "output", effect, "aux");
|
||||
}
|
||||
|
||||
gegl_node_link (src_node, effect);
|
||||
}
|
||||
|
||||
dest_node = gegl_node_new_child (gegl,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", result_buffer,
|
||||
NULL);
|
||||
|
||||
gegl_node_link (effect, dest_node);
|
||||
|
||||
iter = pika_chunk_iterator_new (region);
|
||||
|
||||
if (progress &&
|
||||
/* avoid the interactive iteration interval for area filters (or meta ops
|
||||
* that potentially involve area filters), since their processing speed
|
||||
* tends to be sensitive to the chunk size.
|
||||
*/
|
||||
! pika_gegl_node_is_area_filter_operation (underlying_operation))
|
||||
{
|
||||
/* we use a shorter iteration interval for interactive use (when there's
|
||||
* progress indication), to stay responsive.
|
||||
*/
|
||||
pika_chunk_iterator_set_interval (
|
||||
iter,
|
||||
APPLY_OPERATION_INTERACTIVE_INTERVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we use a longer iteration interval for non-interactive use (when
|
||||
* there's no progress indication), or when applying an area filter (see
|
||||
* above), as this generally allows for faster processing. we don't
|
||||
* avoid chunking altogether, since *some* chunking is still desirable to
|
||||
* reduce the space needed for intermediate results.
|
||||
*/
|
||||
pika_chunk_iterator_set_interval (
|
||||
iter,
|
||||
APPLY_OPERATION_NON_INTERACTIVE_INTERVAL);
|
||||
}
|
||||
|
||||
while (pika_chunk_iterator_next (iter))
|
||||
{
|
||||
GeglRectangle render_rect;
|
||||
|
||||
if (progress)
|
||||
{
|
||||
while (! cancel && g_main_context_pending (NULL))
|
||||
g_main_context_iteration (NULL, FALSE);
|
||||
|
||||
if (cancel)
|
||||
break;
|
||||
}
|
||||
|
||||
while (pika_chunk_iterator_get_rect (iter, &render_rect))
|
||||
{
|
||||
gegl_node_blit (dest_node, 1.0, &render_rect, NULL, NULL, 0,
|
||||
GEGL_BLIT_DEFAULT);
|
||||
|
||||
done_pixels += (gint64) render_rect.width *
|
||||
(gint64) render_rect.height;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
{
|
||||
pika_progress_set_value (progress,
|
||||
(gdouble) done_pixels /
|
||||
(gdouble) all_pixels);
|
||||
}
|
||||
}
|
||||
|
||||
if (result_buffer != dest_buffer)
|
||||
{
|
||||
if (! cancel)
|
||||
pika_gegl_buffer_copy (result_buffer, dest_rect, GEGL_ABYSS_NONE,
|
||||
dest_buffer, dest_rect);
|
||||
|
||||
g_object_unref (result_buffer);
|
||||
}
|
||||
|
||||
gegl_buffer_thaw_changed (dest_buffer);
|
||||
|
||||
g_object_unref (gegl);
|
||||
|
||||
if (operation_src_node)
|
||||
{
|
||||
gegl_node_link (operation_src_node, operation);
|
||||
}
|
||||
|
||||
if (progress_started)
|
||||
{
|
||||
pika_progress_end (progress);
|
||||
|
||||
if (cancelable)
|
||||
g_signal_handlers_disconnect_by_func (progress,
|
||||
pika_gegl_apply_operation_cancel,
|
||||
&cancel);
|
||||
}
|
||||
|
||||
return ! cancel;
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_dither (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
gint levels,
|
||||
gint dither_type)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
levels = CLAMP (levels, 2, 65536);
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:dither",
|
||||
"red-levels", levels,
|
||||
"green-levels", levels,
|
||||
"blue-levels", levels,
|
||||
"alpha-bits", levels,
|
||||
"dither-method", dither_type,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_flatten (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const PikaRGB *background,
|
||||
const Babl *space,
|
||||
PikaLayerColorSpace composite_space)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
g_return_if_fail (background != NULL);
|
||||
|
||||
node = pika_gegl_create_flatten_node (background, space, composite_space);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_feather (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble radius_x,
|
||||
gdouble radius_y,
|
||||
gboolean edge_lock)
|
||||
{
|
||||
GaussianBlurAbyssPolicy abyss_policy;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
if (edge_lock)
|
||||
abyss_policy = GAUSSIAN_BLUR_ABYSS_CLAMP;
|
||||
else
|
||||
abyss_policy = GAUSSIAN_BLUR_ABYSS_NONE;
|
||||
|
||||
/* 3.5 is completely magic and picked to visually match the old
|
||||
* gaussian_blur_region() on a crappy laptop display
|
||||
*/
|
||||
pika_gegl_apply_gaussian_blur (src_buffer,
|
||||
progress, undo_desc,
|
||||
dest_buffer, dest_rect,
|
||||
radius_x / 3.5,
|
||||
radius_y / 3.5,
|
||||
abyss_policy);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_border (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gint radius_x,
|
||||
gint radius_y,
|
||||
PikaChannelBorderStyle style,
|
||||
gboolean edge_lock)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case PIKA_CHANNEL_BORDER_STYLE_HARD:
|
||||
case PIKA_CHANNEL_BORDER_STYLE_FEATHERED:
|
||||
{
|
||||
gboolean feather = style == PIKA_CHANNEL_BORDER_STYLE_FEATHERED;
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "pika:border",
|
||||
"radius-x", radius_x,
|
||||
"radius-y", radius_y,
|
||||
"feather", feather,
|
||||
"edge-lock", edge_lock,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case PIKA_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 "pika: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", "pika:shrink",
|
||||
"radius-x", 1,
|
||||
"radius-y", 1,
|
||||
"edge-lock", edge_lock,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
grow = gegl_node_new_child (node,
|
||||
"operation", "pika:grow",
|
||||
"radius-x", radius_x,
|
||||
"radius-y", radius_y,
|
||||
NULL);
|
||||
shrink = gegl_node_new_child (node,
|
||||
"operation", "pika: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 (shrink, "output", subtract, "aux");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pika_assert_not_reached ();
|
||||
}
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, dest_rect, TRUE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_grow (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "pika:grow",
|
||||
"radius-x", radius_x,
|
||||
"radius-y", radius_y,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, dest_rect, TRUE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_shrink (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "pika:shrink",
|
||||
"radius-x", radius_x,
|
||||
"radius-y", radius_y,
|
||||
"edge-lock", edge_lock,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, dest_rect, TRUE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_flood (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "pika:flood",
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, dest_rect, TRUE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_gaussian_blur (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble std_dev_x,
|
||||
gdouble std_dev_y,
|
||||
GaussianBlurAbyssPolicy abyss_policy)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_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,
|
||||
"abyss-policy", abyss_policy,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, dest_rect, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_invert_gamma (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:invert-gamma",
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_invert_linear (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:invert-linear",
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_opacity (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
g_return_if_fail (mask == NULL || GEGL_IS_BUFFER (mask));
|
||||
|
||||
node = pika_gegl_create_apply_opacity_node (mask,
|
||||
mask_offset_x,
|
||||
mask_offset_y,
|
||||
opacity);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_scale (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
PikaInterpolationType interpolation_type,
|
||||
gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:scale-ratio",
|
||||
"origin-x", 0.0,
|
||||
"origin-y", 0.0,
|
||||
"sampler", interpolation_type,
|
||||
"abyss-policy", GEGL_ABYSS_CLAMP,
|
||||
"x", x,
|
||||
"y", y,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_set_alpha (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "pika:set-alpha",
|
||||
"value", value,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_threshold (GeglBuffer *src_buffer,
|
||||
PikaProgress *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 || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:threshold",
|
||||
"value", value,
|
||||
NULL);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_apply_transform (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
PikaInterpolationType interpolation_type,
|
||||
PikaMatrix3 *transform)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:transform",
|
||||
"near-z", PIKA_TRANSFORM_NEAR_Z,
|
||||
"sampler", interpolation_type,
|
||||
NULL);
|
||||
|
||||
pika_gegl_node_set_matrix (node, transform);
|
||||
|
||||
pika_gegl_apply_operation (src_buffer, progress, undo_desc,
|
||||
node, dest_buffer, NULL, FALSE);
|
||||
g_object_unref (node);
|
||||
}
|
177
app/gegl/pika-gegl-apply-operation.h
Normal file
177
app/gegl/pika-gegl-apply-operation.h
Normal file
@ -0,0 +1,177 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-apply-operation.h
|
||||
* 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_APPLY_OPERATION_H__
|
||||
#define __PIKA_GEGL_APPLY_OPERATION_H__
|
||||
|
||||
|
||||
/* generic functions, also used by the specific ones below */
|
||||
|
||||
void pika_gegl_apply_operation (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglNode *operation,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gboolean crop_input);
|
||||
|
||||
gboolean pika_gegl_apply_cached_operation (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglNode *operation,
|
||||
gboolean connect_src_buffer,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gboolean crop_input,
|
||||
GeglBuffer *cache,
|
||||
const GeglRectangle *valid_rects,
|
||||
gint n_valid_rects,
|
||||
gboolean cancelable);
|
||||
|
||||
|
||||
/* apply specific operations */
|
||||
|
||||
void pika_gegl_apply_dither (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
gint levels,
|
||||
gint dither_type);
|
||||
|
||||
void pika_gegl_apply_flatten (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const PikaRGB *background,
|
||||
const Babl *space,
|
||||
PikaLayerColorSpace composite_space);
|
||||
|
||||
void pika_gegl_apply_feather (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble radius_x,
|
||||
gdouble radius_y,
|
||||
gboolean edge_lock);
|
||||
|
||||
void pika_gegl_apply_border (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gint radius_x,
|
||||
gint radius_y,
|
||||
PikaChannelBorderStyle style,
|
||||
gboolean edge_lock);
|
||||
|
||||
void pika_gegl_apply_grow (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gint radius_x,
|
||||
gint radius_y);
|
||||
|
||||
void pika_gegl_apply_shrink (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gint radius_x,
|
||||
gint radius_y,
|
||||
gboolean edge_lock);
|
||||
|
||||
void pika_gegl_apply_flood (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect);
|
||||
|
||||
/* UGLY: private enum of gegl:gaussian-blur */
|
||||
typedef enum
|
||||
{
|
||||
GAUSSIAN_BLUR_ABYSS_NONE,
|
||||
GAUSSIAN_BLUR_ABYSS_CLAMP
|
||||
} GaussianBlurAbyssPolicy;
|
||||
|
||||
void pika_gegl_apply_gaussian_blur (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble std_dev_x,
|
||||
gdouble std_dev_y,
|
||||
GaussianBlurAbyssPolicy abyss_policy);
|
||||
|
||||
void pika_gegl_apply_invert_gamma (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer);
|
||||
|
||||
void pika_gegl_apply_invert_linear (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer);
|
||||
|
||||
void pika_gegl_apply_opacity (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
GeglBuffer *mask,
|
||||
gint mask_offset_x,
|
||||
gint mask_offset_y,
|
||||
gdouble opacity);
|
||||
|
||||
void pika_gegl_apply_scale (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
PikaInterpolationType interpolation_type,
|
||||
gdouble x,
|
||||
gdouble y);
|
||||
|
||||
void pika_gegl_apply_set_alpha (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
gdouble value);
|
||||
|
||||
void pika_gegl_apply_threshold (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
gdouble value);
|
||||
|
||||
void pika_gegl_apply_transform (GeglBuffer *src_buffer,
|
||||
PikaProgress *progress,
|
||||
const gchar *undo_desc,
|
||||
GeglBuffer *dest_buffer,
|
||||
PikaInterpolationType interpolation_type,
|
||||
PikaMatrix3 *transform);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_APPLY_OPERATION_H__ */
|
44
app/gegl/pika-gegl-enums.c
Normal file
44
app/gegl/pika-gegl-enums.c
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
/* Generated data (by pika-mkenums) */
|
||||
|
||||
#include "stamp-pika-gegl-enums.h"
|
||||
#include "config.h"
|
||||
#include <gio/gio.h>
|
||||
#include "libpikabase/pikabase.h"
|
||||
#include "core/core-enums.h"
|
||||
#include "pika-gegl-enums.h"
|
||||
#include "pika-intl.h"
|
||||
|
||||
/* enumerations from "pika-gegl-enums.h" */
|
||||
GType
|
||||
pika_cage_mode_get_type (void)
|
||||
{
|
||||
static const GEnumValue values[] =
|
||||
{
|
||||
{ PIKA_CAGE_MODE_CAGE_CHANGE, "PIKA_CAGE_MODE_CAGE_CHANGE", "cage-change" },
|
||||
{ PIKA_CAGE_MODE_DEFORM, "PIKA_CAGE_MODE_DEFORM", "deform" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static const PikaEnumDesc descs[] =
|
||||
{
|
||||
{ PIKA_CAGE_MODE_CAGE_CHANGE, NC_("cage-mode", "Create or adjust the cage"), NULL },
|
||||
{ PIKA_CAGE_MODE_DEFORM, NC_("cage-mode", "Deform the cage\nto deform the image"), NULL },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static GType type = 0;
|
||||
|
||||
if (G_UNLIKELY (! type))
|
||||
{
|
||||
type = g_enum_register_static ("PikaCageMode", values);
|
||||
pika_type_set_translation_context (type, "cage-mode");
|
||||
pika_enum_set_value_descriptions (type, descs);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/* Generated data ends here */
|
||||
|
39
app/gegl/pika-gegl-enums.h
Normal file
39
app/gegl/pika-gegl-enums.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-enums.h
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_ENUMS_H__
|
||||
#define __PIKA_GEGL_ENUMS_H__
|
||||
|
||||
|
||||
#define PIKA_TYPE_CAGE_MODE (pika_cage_mode_get_type ())
|
||||
|
||||
GType pika_cage_mode_get_type (void) G_GNUC_CONST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PIKA_CAGE_MODE_CAGE_CHANGE, /*< desc="Create or adjust the cage" >*/
|
||||
PIKA_CAGE_MODE_DEFORM /*< desc="Deform the cage\nto deform the image" >*/
|
||||
} PikaCageMode;
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_ENUMS_H__ */
|
131
app/gegl/pika-gegl-loops-sse2.c
Normal file
131
app/gegl/pika-gegl-loops-sse2.c
Normal file
@ -0,0 +1,131 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-loops-sse2.c
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "pika-gegl-loops-sse2.h"
|
||||
|
||||
|
||||
#if COMPILE_SSE2_INTRINISICS
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
|
||||
/* helper function of pika_gegl_smudge_with_paint_process_sse2()
|
||||
* src and dest can be the same address
|
||||
*/
|
||||
static inline void
|
||||
pika_gegl_smudge_with_paint_blend_sse2 (const gfloat *src1,
|
||||
gfloat src1_rate,
|
||||
const gfloat *src2,
|
||||
gfloat src2_rate,
|
||||
gfloat *dest,
|
||||
gboolean no_erasing_src2)
|
||||
{
|
||||
/* 2017/4/13 shark0r : According to my test, SSE decreases about 25%
|
||||
* execution time
|
||||
*/
|
||||
|
||||
__m128 v_src1 = _mm_loadu_ps (src1);
|
||||
__m128 v_src2 = _mm_loadu_ps (src2);
|
||||
__m128 *v_dest = (__v4sf *) dest;
|
||||
|
||||
gfloat orginal_src2_alpha;
|
||||
gfloat src1_alpha;
|
||||
gfloat src2_alpha;
|
||||
gfloat result_alpha;
|
||||
|
||||
orginal_src2_alpha = v_src2[3];
|
||||
src1_alpha = src1_rate * v_src1[3];
|
||||
src2_alpha = src2_rate * orginal_src2_alpha;
|
||||
result_alpha = src1_alpha + src2_alpha;
|
||||
|
||||
if (result_alpha == 0)
|
||||
{
|
||||
*v_dest = _mm_set1_ps (0);
|
||||
return;
|
||||
}
|
||||
|
||||
*v_dest = (v_src1 * _mm_set1_ps (src1_alpha) +
|
||||
v_src2 * _mm_set1_ps (src2_alpha)) /
|
||||
_mm_set1_ps (result_alpha);
|
||||
|
||||
if (no_erasing_src2)
|
||||
{
|
||||
result_alpha = MAX (result_alpha, orginal_src2_alpha);
|
||||
}
|
||||
|
||||
dest[3] = result_alpha;
|
||||
}
|
||||
|
||||
/* helper function of pika_gegl_smudge_with_paint()
|
||||
*
|
||||
* note that it's the caller's responsibility to verify that the buffers are
|
||||
* properly aligned
|
||||
*/
|
||||
void
|
||||
pika_gegl_smudge_with_paint_process_sse2 (gfloat *accum,
|
||||
const gfloat *canvas,
|
||||
gfloat *paint,
|
||||
gint count,
|
||||
const gfloat *brush_color,
|
||||
gfloat brush_a,
|
||||
gboolean no_erasing,
|
||||
gfloat flow,
|
||||
gfloat rate)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
/* blend accum_buffer and canvas_buffer to accum_buffer */
|
||||
pika_gegl_smudge_with_paint_blend_sse2 (accum, rate, canvas, 1 - rate,
|
||||
accum, no_erasing);
|
||||
|
||||
/* blend accum_buffer and brush color/pixmap to paint_buffer */
|
||||
if (brush_a == 0) /* pure smudge */
|
||||
{
|
||||
memcpy (paint, accum, sizeof (gfloat) * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
const gfloat *src1 = brush_color ? brush_color : paint;
|
||||
|
||||
pika_gegl_smudge_with_paint_blend_sse2 (src1, flow, accum, 1 - flow,
|
||||
paint, no_erasing);
|
||||
}
|
||||
|
||||
accum += 4;
|
||||
canvas += 4;
|
||||
paint += 4;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* COMPILE_SSE2_INTRINISICS */
|
44
app/gegl/pika-gegl-loops-sse2.h
Normal file
44
app/gegl/pika-gegl-loops-sse2.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-loops-sse2.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_LOOPS_SSE2_H__
|
||||
#define __PIKA_GEGL_LOOPS_SSE2_H__
|
||||
|
||||
|
||||
#if COMPILE_SSE2_INTRINISICS
|
||||
|
||||
void pika_gegl_smudge_with_paint_process_sse2 (gfloat *accum,
|
||||
const gfloat *canvas,
|
||||
gfloat *paint,
|
||||
gint count,
|
||||
const gfloat *brush_color,
|
||||
gfloat brush_a,
|
||||
gboolean no_erasing,
|
||||
gfloat flow,
|
||||
gfloat rate);
|
||||
|
||||
#endif /* COMPILE_SSE2_INTRINISICS */
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_LOOPS_SSE2_H__ */
|
1106
app/gegl/pika-gegl-loops.cc
Normal file
1106
app/gegl/pika-gegl-loops.cc
Normal file
File diff suppressed because it is too large
Load Diff
113
app/gegl/pika-gegl-loops.h
Normal file
113
app/gegl/pika-gegl-loops.h
Normal file
@ -0,0 +1,113 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-loops.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_LOOPS_H__
|
||||
#define __PIKA_GEGL_LOOPS_H__
|
||||
|
||||
|
||||
void pika_gegl_buffer_copy (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
GeglAbyssPolicy abyss_policy,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect);
|
||||
|
||||
void pika_gegl_clear (GeglBuffer *buffer,
|
||||
const GeglRectangle *rect);
|
||||
|
||||
/* this is a pretty stupid port of concolve_region() that only works
|
||||
* on a linear source buffer
|
||||
*/
|
||||
void pika_gegl_convolve (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
const gfloat *kernel,
|
||||
gint kernel_size,
|
||||
gdouble divisor,
|
||||
PikaConvolutionType mode,
|
||||
gboolean alpha_weighting);
|
||||
|
||||
void pika_gegl_dodgeburn (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble exposure,
|
||||
PikaDodgeBurnType type,
|
||||
PikaTransferMode mode);
|
||||
|
||||
void pika_gegl_smudge_with_paint (GeglBuffer *accum_buffer,
|
||||
const GeglRectangle *accum_rect,
|
||||
GeglBuffer *canvas_buffer,
|
||||
const GeglRectangle *canvas_rect,
|
||||
const PikaRGB *brush_color,
|
||||
GeglBuffer *paint_buffer,
|
||||
gboolean no_erasing,
|
||||
gdouble flow,
|
||||
gdouble rate);
|
||||
|
||||
void pika_gegl_apply_mask (GeglBuffer *mask_buffer,
|
||||
const GeglRectangle *mask_rect,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble opacity);
|
||||
|
||||
void pika_gegl_combine_mask (GeglBuffer *mask_buffer,
|
||||
const GeglRectangle *mask_rect,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble opacity);
|
||||
|
||||
void pika_gegl_combine_mask_weird (GeglBuffer *mask_buffer,
|
||||
const GeglRectangle *mask_rect,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
gdouble opacity,
|
||||
gboolean stipple);
|
||||
|
||||
void pika_gegl_index_to_mask (GeglBuffer *indexed_buffer,
|
||||
const GeglRectangle *indexed_rect,
|
||||
const Babl *indexed_format,
|
||||
GeglBuffer *mask_buffer,
|
||||
const GeglRectangle *mask_rect,
|
||||
gint index);
|
||||
|
||||
void pika_gegl_convert_color_profile (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
PikaColorProfile *src_profile,
|
||||
GeglBuffer *dest_buffer,
|
||||
const GeglRectangle *dest_rect,
|
||||
PikaColorProfile *dest_profile,
|
||||
PikaColorRenderingIntent intent,
|
||||
gboolean bpc,
|
||||
PikaProgress *progress);
|
||||
|
||||
void pika_gegl_average_color (GeglBuffer *buffer,
|
||||
const GeglRectangle *rect,
|
||||
gboolean clip_to_buffer,
|
||||
GeglAbyssPolicy abyss_policy,
|
||||
const Babl *format,
|
||||
gpointer color);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_LOOPS_H__ */
|
657
app/gegl/pika-gegl-mask-combine.cc
Normal file
657
app/gegl/pika-gegl-mask-combine.cc
Normal file
@ -0,0 +1,657 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "libpikabase/pikabase.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "pika-babl.h"
|
||||
#include "pika-gegl-loops.h"
|
||||
#include "pika-gegl-mask-combine.h"
|
||||
|
||||
|
||||
#define EPSILON 1e-6
|
||||
|
||||
#define PIXELS_PER_THREAD \
|
||||
(/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
|
||||
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_combine_rect (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h)
|
||||
{
|
||||
GeglRectangle rect;
|
||||
gfloat value;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
|
||||
|
||||
if (! gegl_rectangle_intersect (&rect,
|
||||
GEGL_RECTANGLE (x, y, w, h),
|
||||
gegl_buffer_get_abyss (mask)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case PIKA_CHANNEL_OP_REPLACE:
|
||||
case PIKA_CHANNEL_OP_ADD:
|
||||
value = 1.0f;
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_SUBTRACT:
|
||||
value = 0.0f;
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_INTERSECT:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gegl_buffer_set_color_from_pixel (mask, &rect, &value,
|
||||
babl_format ("Y float"));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_combine_ellipse (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h,
|
||||
gboolean antialias)
|
||||
{
|
||||
return pika_gegl_mask_combine_ellipse_rect (mask, op, x, y, w, h,
|
||||
w / 2.0, h / 2.0, antialias);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_combine_ellipse_rect (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h,
|
||||
gdouble rx,
|
||||
gdouble ry,
|
||||
gboolean antialias)
|
||||
{
|
||||
GeglRectangle rect;
|
||||
const Babl *format;
|
||||
gint bpp;
|
||||
gfloat one_f = 1.0f;
|
||||
gpointer one;
|
||||
gdouble cx;
|
||||
gdouble cy;
|
||||
gint left;
|
||||
gint right;
|
||||
gint top;
|
||||
gint bottom;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
|
||||
|
||||
if (rx <= EPSILON || ry <= EPSILON)
|
||||
return pika_gegl_mask_combine_rect (mask, op, x, y, w, h);
|
||||
|
||||
left = x;
|
||||
right = x + w;
|
||||
top = y;
|
||||
bottom = y + h;
|
||||
|
||||
cx = (left + right) / 2.0;
|
||||
cy = (top + bottom) / 2.0;
|
||||
|
||||
rx = MIN (rx, w / 2.0);
|
||||
ry = MIN (ry, h / 2.0);
|
||||
|
||||
if (! gegl_rectangle_intersect (&rect,
|
||||
GEGL_RECTANGLE (x, y, w, h),
|
||||
gegl_buffer_get_abyss (mask)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
format = gegl_buffer_get_format (mask);
|
||||
|
||||
if (antialias)
|
||||
{
|
||||
format = pika_babl_format_change_component_type (
|
||||
format, PIKA_COMPONENT_TYPE_FLOAT);
|
||||
}
|
||||
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
one = g_alloca (bpp);
|
||||
|
||||
babl_process (babl_fish ("Y float", format), &one_f, one, 1);
|
||||
|
||||
/* coordinate-system transforms. (x, y) coordinates are in the image
|
||||
* coordinate-system, and (u, v) coordinates are in a coordinate-system
|
||||
* aligned with the center of one of the elliptic corners, with the positive
|
||||
* directions pointing away from the rectangle. when converting from (x, y)
|
||||
* to (u, v), we use the closest elliptic corner.
|
||||
*/
|
||||
auto x_to_u = [=] (gdouble x)
|
||||
{
|
||||
if (x < cx)
|
||||
return (left + rx) - x;
|
||||
else
|
||||
return x - (right - rx);
|
||||
};
|
||||
|
||||
auto y_to_v = [=] (gdouble y)
|
||||
{
|
||||
if (y < cy)
|
||||
return (top + ry) - y;
|
||||
else
|
||||
return y - (bottom - ry);
|
||||
};
|
||||
|
||||
auto u_to_x_left = [=] (gdouble u)
|
||||
{
|
||||
return (left + rx) - u;
|
||||
};
|
||||
|
||||
auto u_to_x_right = [=] (gdouble u)
|
||||
{
|
||||
return (right - rx) + u;
|
||||
};
|
||||
|
||||
/* intersection of a horizontal line with the ellipse */
|
||||
auto v_to_u = [=] (gdouble v)
|
||||
{
|
||||
if (v > 0.0)
|
||||
return sqrt (MAX (SQR (rx) - SQR (rx * v / ry), 0.0));
|
||||
else
|
||||
return rx;
|
||||
};
|
||||
|
||||
/* intersection of a vertical line with the ellipse */
|
||||
auto u_to_v = [=] (gdouble u)
|
||||
{
|
||||
if (u > 0.0)
|
||||
return sqrt (MAX (SQR (ry) - SQR (ry * u / rx), 0.0));
|
||||
else
|
||||
return ry;
|
||||
};
|
||||
|
||||
/* signed, normalized distance of a point from the ellipse's circumference.
|
||||
* the sign of the result determines if the point is inside (positive) or
|
||||
* outside (negative) the ellipse. the result is normalized to the cross-
|
||||
* section length of a pixel, in the direction of the closest point along the
|
||||
* ellipse.
|
||||
*
|
||||
* we use the following method to approximate the distance: pass horizontal
|
||||
* and vertical lines at the given point, P, and find their (positive) points
|
||||
* of intersection with the ellipse, A and B. the segment AB is an
|
||||
* approximation of the corresponding elliptic arc (see bug #147836). find
|
||||
* the closest point, C, to P, along the segment AB. find the (positive)
|
||||
* point of intersection, Q, of the line PC and the ellipse. Q is an
|
||||
* approximation for the closest point to P along the ellipse, and the
|
||||
* approximated distance is the distance from P to Q.
|
||||
*/
|
||||
auto ellipse_distance = [=] (gdouble u,
|
||||
gdouble v)
|
||||
{
|
||||
gdouble du;
|
||||
gdouble dv;
|
||||
gdouble t;
|
||||
gdouble a, b, c;
|
||||
gdouble d;
|
||||
|
||||
u = MAX (u, 0.0);
|
||||
v = MAX (v, 0.0);
|
||||
|
||||
du = v_to_u (v) - u;
|
||||
dv = u_to_v (u) - v;
|
||||
|
||||
t = SQR (du) / (SQR (du) + SQR (dv));
|
||||
|
||||
du *= 1.0 - t;
|
||||
dv *= t;
|
||||
|
||||
v *= rx / ry;
|
||||
dv *= rx / ry;
|
||||
|
||||
a = SQR (du) + SQR (dv);
|
||||
b = u * du + v * dv;
|
||||
c = SQR (u) + SQR (v) - SQR (rx);
|
||||
|
||||
if (a <= EPSILON)
|
||||
return 0.0;
|
||||
|
||||
if (c < 0.0)
|
||||
t = (-b + sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
|
||||
else
|
||||
t = (-b - sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
|
||||
|
||||
dv *= ry / rx;
|
||||
|
||||
d = sqrt (SQR (du * t) + SQR (dv * t));
|
||||
|
||||
if (c > 0.0)
|
||||
d = -d;
|
||||
|
||||
d /= sqrt (SQR (MIN (du / dv, dv / du)) + 1.0);
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
/* anti-aliased value of a pixel */
|
||||
auto pixel_value = [=] (gint x,
|
||||
gint y)
|
||||
{
|
||||
gdouble u = x_to_u (x + 0.5);
|
||||
gdouble v = y_to_v (y + 0.5);
|
||||
gdouble d = ellipse_distance (u, v);
|
||||
|
||||
/* use the distance of the pixel's center from the ellipse to approximate
|
||||
* the coverage
|
||||
*/
|
||||
d = CLAMP (0.5 + d, 0.0, 1.0);
|
||||
|
||||
/* we're at the horizontal boundary of an elliptic corner */
|
||||
if (u < 0.5)
|
||||
d = d * (0.5 + u) + (0.5 - u);
|
||||
|
||||
/* we're at the vertical boundary of an elliptic corner */
|
||||
if (v < 0.5)
|
||||
d = d * (0.5 + v) + (0.5 - v);
|
||||
|
||||
/* opposite horizontal corners intersect the pixel */
|
||||
if (x == (right - 1) - (x - left))
|
||||
d = 2.0 * d - 1.0;
|
||||
|
||||
/* opposite vertical corners intersect the pixel */
|
||||
if (y == (bottom - 1) - (y - top))
|
||||
d = 2.0 * d - 1.0;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
auto ellipse_range = [=] (gdouble y,
|
||||
gdouble *x0,
|
||||
gdouble *x1)
|
||||
{
|
||||
gdouble u = v_to_u (y_to_v (y));
|
||||
|
||||
*x0 = u_to_x_left (u);
|
||||
*x1 = u_to_x_right (u);
|
||||
};
|
||||
|
||||
auto fill0 = [=] (gpointer dest,
|
||||
gint n)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case PIKA_CHANNEL_OP_REPLACE:
|
||||
case PIKA_CHANNEL_OP_INTERSECT:
|
||||
memset (dest, 0, bpp * n);
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_ADD:
|
||||
case PIKA_CHANNEL_OP_SUBTRACT:
|
||||
break;
|
||||
}
|
||||
|
||||
return (gpointer) ((guint8 *) dest + bpp * n);
|
||||
};
|
||||
|
||||
auto fill1 = [=] (gpointer dest,
|
||||
gint n)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case PIKA_CHANNEL_OP_REPLACE:
|
||||
case PIKA_CHANNEL_OP_ADD:
|
||||
gegl_memset_pattern (dest, one, bpp, n);
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_SUBTRACT:
|
||||
memset (dest, 0, bpp * n);
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_INTERSECT:
|
||||
break;
|
||||
}
|
||||
|
||||
return (gpointer) ((guint8 *) dest + bpp * n);
|
||||
};
|
||||
|
||||
auto set = [=] (gpointer dest,
|
||||
gfloat value)
|
||||
{
|
||||
gfloat *p = (gfloat *) dest;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case PIKA_CHANNEL_OP_REPLACE:
|
||||
*p = value;
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_ADD:
|
||||
*p = MIN (*p + value, 1.0);
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_SUBTRACT:
|
||||
*p = MAX (*p - value, 0.0);
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_INTERSECT:
|
||||
*p = MIN (*p, value);
|
||||
break;
|
||||
}
|
||||
|
||||
return (gpointer) (p + 1);
|
||||
};
|
||||
|
||||
gegl_parallel_distribute_area (
|
||||
&rect, PIXELS_PER_THREAD,
|
||||
[=] (const GeglRectangle *area)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
iter = gegl_buffer_iterator_new (
|
||||
mask, area, 0, format,
|
||||
op == PIKA_CHANNEL_OP_REPLACE ? GEGL_ACCESS_WRITE :
|
||||
GEGL_ACCESS_READWRITE,
|
||||
GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
const GeglRectangle *roi = &iter->items[0].roi;
|
||||
gpointer d = iter->items[0].data;
|
||||
gdouble tx0, ty0;
|
||||
gdouble tx1, ty1;
|
||||
gdouble x0;
|
||||
gdouble x1;
|
||||
gint y;
|
||||
|
||||
/* tile bounds */
|
||||
tx0 = roi->x;
|
||||
ty0 = roi->y;
|
||||
|
||||
tx1 = roi->x + roi->width;
|
||||
ty1 = roi->y + roi->height;
|
||||
|
||||
if (! antialias)
|
||||
{
|
||||
tx0 += 0.5;
|
||||
ty0 += 0.5;
|
||||
|
||||
tx1 -= 0.5;
|
||||
ty1 -= 0.5;
|
||||
}
|
||||
|
||||
/* if the tile is fully inside/outside the ellipse, fill it with 1/0,
|
||||
* respectively, and skip the rest.
|
||||
*/
|
||||
ellipse_range (ty0, &x0, &x1);
|
||||
|
||||
if (tx0 >= x0 && tx1 <= x1)
|
||||
{
|
||||
ellipse_range (ty1, &x0, &x1);
|
||||
|
||||
if (tx0 >= x0 && tx1 <= x1)
|
||||
{
|
||||
fill1 (d, iter->length);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (tx1 < x0 || tx0 > x1)
|
||||
{
|
||||
ellipse_range (ty1, &x0, &x1);
|
||||
|
||||
if (tx1 < x0 || tx0 > x1)
|
||||
{
|
||||
if ((ty0 - cy) * (ty1 - cy) >= 0.0)
|
||||
{
|
||||
fill0 (d, iter->length);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (y = roi->y; y < roi->y + roi->height; y++)
|
||||
{
|
||||
gint a, b;
|
||||
|
||||
if (antialias)
|
||||
{
|
||||
gdouble v = y_to_v (y + 0.5);
|
||||
gdouble u0 = v_to_u (v - 0.5);
|
||||
gdouble u1 = v_to_u (v + 0.5);
|
||||
gint x;
|
||||
|
||||
a = floor (u_to_x_left (u0)) - roi->x;
|
||||
a = CLAMP (a, 0, roi->width);
|
||||
|
||||
b = ceil (u_to_x_left (u1)) - roi->x;
|
||||
b = CLAMP (b, a, roi->width);
|
||||
|
||||
d = fill0 (d, a);
|
||||
|
||||
for (x = roi->x + a; x < roi->x + b; x++)
|
||||
d = set (d, pixel_value (x, y));
|
||||
|
||||
a = floor (u_to_x_right (u1)) - roi->x;
|
||||
a = CLAMP (a, b, roi->width);
|
||||
|
||||
d = fill1 (d, a - b);
|
||||
|
||||
b = ceil (u_to_x_right (u0)) - roi->x;
|
||||
b = CLAMP (b, a, roi->width);
|
||||
|
||||
for (x = roi->x + a; x < roi->x + b; x++)
|
||||
d = set (d, pixel_value (x, y));
|
||||
|
||||
d = fill0 (d, roi->width - b);
|
||||
}
|
||||
else
|
||||
{
|
||||
ellipse_range (y + 0.5, &x0, &x1);
|
||||
|
||||
a = ceil (x0 - 0.5) - roi->x;
|
||||
a = CLAMP (a, 0, roi->width);
|
||||
|
||||
b = floor (x1 + 0.5) - roi->x;
|
||||
b = CLAMP (b, 0, roi->width);
|
||||
|
||||
d = fill0 (d, a);
|
||||
d = fill1 (d, b - a);
|
||||
d = fill0 (d, roi->width - b);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_combine_buffer (GeglBuffer *mask,
|
||||
GeglBuffer *add_on,
|
||||
PikaChannelOps op,
|
||||
gint off_x,
|
||||
gint off_y)
|
||||
{
|
||||
GeglRectangle mask_rect;
|
||||
GeglRectangle add_on_rect;
|
||||
const Babl *mask_format;
|
||||
const Babl *add_on_format;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (add_on), FALSE);
|
||||
|
||||
if (! gegl_rectangle_intersect (&mask_rect,
|
||||
GEGL_RECTANGLE (
|
||||
off_x + gegl_buffer_get_x (add_on),
|
||||
off_y + gegl_buffer_get_y (add_on),
|
||||
gegl_buffer_get_width (add_on),
|
||||
gegl_buffer_get_height (add_on)),
|
||||
gegl_buffer_get_abyss (mask)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
add_on_rect = mask_rect;
|
||||
add_on_rect.x -= off_x;
|
||||
add_on_rect.y -= off_y;
|
||||
|
||||
mask_format = gegl_buffer_get_format (mask);
|
||||
add_on_format = gegl_buffer_get_format (add_on);
|
||||
|
||||
if (op == PIKA_CHANNEL_OP_REPLACE &&
|
||||
(pika_babl_is_bounded (pika_babl_format_get_precision (add_on_format)) ||
|
||||
pika_babl_is_bounded (pika_babl_format_get_precision (mask_format))))
|
||||
{
|
||||
/* See below: this additional hack is only needed for the
|
||||
* pika-channel-combine-masks procedure, it's the only place that
|
||||
* allows to combine arbitrary channels with each other.
|
||||
*/
|
||||
gegl_buffer_set_format (
|
||||
add_on,
|
||||
pika_babl_format_change_trc (
|
||||
add_on_format, pika_babl_format_get_trc (mask_format)));
|
||||
|
||||
pika_gegl_buffer_copy (add_on, &add_on_rect, GEGL_ABYSS_NONE,
|
||||
mask, &mask_rect);
|
||||
|
||||
gegl_buffer_set_format (add_on, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* This is a hack: all selections/layer masks/channels are always
|
||||
* linear except for channels in 8-bit images. We don't want these
|
||||
* "Y' u8" to be converted to "Y float" because that would cause a
|
||||
* gamma canversion and give unexpected results for
|
||||
* "add/subtract/etc channel from selection". Instead, use all
|
||||
* channel values "as-is", which makes no differce except in the
|
||||
* 8-bit case where we need it.
|
||||
*
|
||||
* See https://bugzilla.gnome.org/show_bug.cgi?id=791519
|
||||
*/
|
||||
mask_format = pika_babl_format_change_component_type (
|
||||
mask_format, PIKA_COMPONENT_TYPE_FLOAT);
|
||||
|
||||
add_on_format = pika_babl_format_change_component_type (
|
||||
add_on_format, PIKA_COMPONENT_TYPE_FLOAT);
|
||||
|
||||
gegl_parallel_distribute_area (
|
||||
&mask_rect, PIXELS_PER_THREAD,
|
||||
[=] (const GeglRectangle *mask_area)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
GeglRectangle add_on_area;
|
||||
|
||||
add_on_area = *mask_area;
|
||||
add_on_area.x -= off_x;
|
||||
add_on_area.y -= off_y;
|
||||
|
||||
iter = gegl_buffer_iterator_new (mask, mask_area, 0,
|
||||
mask_format,
|
||||
op == PIKA_CHANNEL_OP_REPLACE ?
|
||||
GEGL_ACCESS_WRITE :
|
||||
GEGL_ACCESS_READWRITE,
|
||||
GEGL_ABYSS_NONE, 2);
|
||||
|
||||
gegl_buffer_iterator_add (iter, add_on, &add_on_area, 0,
|
||||
add_on_format,
|
||||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
|
||||
|
||||
auto process = [=] (auto value)
|
||||
{
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
gfloat *mask_data = (gfloat *) iter->items[0].data;
|
||||
const gfloat *add_on_data = (const gfloat *) iter->items[1].data;
|
||||
gint count = iter->length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
const gfloat val = value (mask_data, add_on_data);
|
||||
|
||||
*mask_data = CLAMP (val, 0.0f, 1.0f);
|
||||
|
||||
add_on_data++;
|
||||
mask_data++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case PIKA_CHANNEL_OP_REPLACE:
|
||||
process ([] (const gfloat *mask,
|
||||
const gfloat *add_on)
|
||||
{
|
||||
return *add_on;
|
||||
});
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_ADD:
|
||||
process ([] (const gfloat *mask,
|
||||
const gfloat *add_on)
|
||||
{
|
||||
return *mask + *add_on;
|
||||
});
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_SUBTRACT:
|
||||
process ([] (const gfloat *mask,
|
||||
const gfloat *add_on)
|
||||
{
|
||||
return *mask - *add_on;
|
||||
});
|
||||
break;
|
||||
|
||||
case PIKA_CHANNEL_OP_INTERSECT:
|
||||
process ([] (const gfloat *mask,
|
||||
const gfloat *add_on)
|
||||
{
|
||||
return MIN (*mask, *add_on);
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
55
app/gegl/pika-gegl-mask-combine.h
Normal file
55
app/gegl/pika-gegl-mask-combine.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_MASK_COMBINE_H__
|
||||
#define __PIKA_GEGL_MASK_COMBINE_H__
|
||||
|
||||
|
||||
gboolean pika_gegl_mask_combine_rect (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h);
|
||||
gboolean pika_gegl_mask_combine_ellipse (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h,
|
||||
gboolean antialias);
|
||||
gboolean pika_gegl_mask_combine_ellipse_rect (GeglBuffer *mask,
|
||||
PikaChannelOps op,
|
||||
gint x,
|
||||
gint y,
|
||||
gint w,
|
||||
gint h,
|
||||
gdouble rx,
|
||||
gdouble ry,
|
||||
gboolean antialias);
|
||||
gboolean pika_gegl_mask_combine_buffer (GeglBuffer *mask,
|
||||
GeglBuffer *add_on,
|
||||
PikaChannelOps op,
|
||||
gint off_x,
|
||||
gint off_y);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_MASK_COMBINE_H__ */
|
257
app/gegl/pika-gegl-mask.c
Normal file
257
app/gegl/pika-gegl-mask.c
Normal file
@ -0,0 +1,257 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "gegl/pika-gegl-mask.h"
|
||||
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_bounds (GeglBuffer *buffer,
|
||||
gint *x1,
|
||||
gint *y1,
|
||||
gint *x2,
|
||||
gint *y2)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
const GeglRectangle *extent;
|
||||
const GeglRectangle *roi;
|
||||
const Babl *format;
|
||||
gint bpp;
|
||||
gint tx1, tx2, ty1, ty2;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
|
||||
g_return_val_if_fail (x1 != NULL, FALSE);
|
||||
g_return_val_if_fail (y1 != NULL, FALSE);
|
||||
g_return_val_if_fail (x2 != NULL, FALSE);
|
||||
g_return_val_if_fail (y2 != NULL, FALSE);
|
||||
|
||||
extent = gegl_buffer_get_extent (buffer);
|
||||
|
||||
/* go through and calculate the bounds */
|
||||
tx1 = extent->x + extent->width;
|
||||
ty1 = extent->y + extent->height;
|
||||
tx2 = extent->x;
|
||||
ty2 = extent->y;
|
||||
|
||||
format = gegl_buffer_get_format (buffer);
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
|
||||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
|
||||
roi = &iter->items[0].roi;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
const guint8 *data_u8 = iter->items[0].data;
|
||||
gint ex = roi->x + roi->width;
|
||||
gint ey = roi->y + roi->height;
|
||||
|
||||
/* only check the pixels if this tile is not fully within the
|
||||
* currently computed bounds
|
||||
*/
|
||||
if (roi->x < tx1 || ex > tx2 ||
|
||||
roi->y < ty1 || ey > ty2)
|
||||
{
|
||||
/* Check upper left and lower right corners to see if we can
|
||||
* avoid checking the rest of the pixels in this tile
|
||||
*/
|
||||
if (! gegl_memeq_zero (data_u8, bpp) &&
|
||||
! gegl_memeq_zero (data_u8 + (iter->length - 1) * bpp, bpp))
|
||||
{
|
||||
/* "ex/ey - 1" because the internal variables are the
|
||||
* right/bottom pixel of the mask's contents, not one
|
||||
* right/below it like the return values.
|
||||
*/
|
||||
|
||||
if (roi->x < tx1) tx1 = roi->x;
|
||||
if (ex > tx2) tx2 = ex - 1;
|
||||
|
||||
if (roi->y < ty1) ty1 = roi->y;
|
||||
if (ey > ty2) ty2 = ey - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
#define FIND_BOUNDS(bpp, type) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
const type *data; \
|
||||
gint y; \
|
||||
\
|
||||
if ((guintptr) data_u8 % bpp) \
|
||||
goto generic; \
|
||||
\
|
||||
data = (const type *) data_u8; \
|
||||
\
|
||||
for (y = roi->y; y < ey; y++) \
|
||||
{ \
|
||||
gint x1; \
|
||||
\
|
||||
for (x1 = 0; x1 < roi->width; x1++) \
|
||||
{ \
|
||||
if (data[x1]) \
|
||||
{ \
|
||||
gint x2; \
|
||||
gint x2_end = MAX (x1, tx2 - roi->x); \
|
||||
\
|
||||
for (x2 = roi->width - 1; x2 > x2_end; x2--) \
|
||||
{ \
|
||||
if (data[x2]) \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
x1 += roi->x; \
|
||||
x2 += roi->x; \
|
||||
\
|
||||
if (x1 < tx1) tx1 = x1; \
|
||||
if (x2 > tx2) tx2 = x2; \
|
||||
\
|
||||
if (y < ty1) ty1 = y; \
|
||||
if (y > ty2) ty2 = y; \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
data += roi->width; \
|
||||
} \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
switch (bpp)
|
||||
{
|
||||
case 1:
|
||||
FIND_BOUNDS (1, guint8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
FIND_BOUNDS (2, guint16);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
FIND_BOUNDS (4, guint32);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
FIND_BOUNDS (8, guint64);
|
||||
break;
|
||||
|
||||
default:
|
||||
generic:
|
||||
{
|
||||
const guint8 *data = data_u8;
|
||||
gint y;
|
||||
|
||||
for (y = roi->y; y < ey; y++)
|
||||
{
|
||||
gint x1;
|
||||
|
||||
for (x1 = 0; x1 < roi->width; x1++)
|
||||
{
|
||||
if (! gegl_memeq_zero (data + x1 * bpp, bpp))
|
||||
{
|
||||
gint x2;
|
||||
gint x2_end = MAX (x1, tx2 - roi->x);
|
||||
|
||||
for (x2 = roi->width - 1; x2 > x2_end; x2--)
|
||||
{
|
||||
if (! gegl_memeq_zero (data + x2 * bpp,
|
||||
bpp))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
x1 += roi->x;
|
||||
x2 += roi->x;
|
||||
|
||||
if (x1 < tx1) tx1 = x1;
|
||||
if (x2 > tx2) tx2 = x2;
|
||||
|
||||
if (y < ty1) ty1 = y;
|
||||
if (y > ty2) ty2 = y;
|
||||
}
|
||||
}
|
||||
|
||||
data += roi->width * bpp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#undef FIND_BOUNDS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx2 = CLAMP (tx2 + 1, 0, gegl_buffer_get_width (buffer));
|
||||
ty2 = CLAMP (ty2 + 1, 0, gegl_buffer_get_height (buffer));
|
||||
|
||||
if (tx1 == gegl_buffer_get_width (buffer) &&
|
||||
ty1 == gegl_buffer_get_height (buffer))
|
||||
{
|
||||
*x1 = 0;
|
||||
*y1 = 0;
|
||||
*x2 = gegl_buffer_get_width (buffer);
|
||||
*y2 = gegl_buffer_get_height (buffer);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*x1 = tx1;
|
||||
*y1 = ty1;
|
||||
*x2 = tx2;
|
||||
*y2 = ty2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_mask_is_empty (GeglBuffer *buffer)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
const Babl *format;
|
||||
gint bpp;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
|
||||
|
||||
format = gegl_buffer_get_format (buffer);
|
||||
bpp = babl_format_get_bytes_per_pixel (format);
|
||||
|
||||
iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
|
||||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
if (! gegl_memeq_zero (iter->items[0].data, bpp * iter->length))
|
||||
{
|
||||
gegl_buffer_iterator_stop (iter);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
34
app/gegl/pika-gegl-mask.h
Normal file
34
app/gegl/pika-gegl-mask.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_MASK_H__
|
||||
#define __PIKA_GEGL_MASK_H__
|
||||
|
||||
|
||||
gboolean pika_gegl_mask_bounds (GeglBuffer *buffer,
|
||||
gint *x1,
|
||||
gint *y1,
|
||||
gint *x2,
|
||||
gint *y2);
|
||||
gboolean pika_gegl_mask_is_empty (GeglBuffer *buffer);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_MASK_H__ */
|
259
app/gegl/pika-gegl-nodes.c
Normal file
259
app/gegl/pika-gegl-nodes.c
Normal file
@ -0,0 +1,259 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-nodes.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "operations/layer-modes/pika-layer-modes.h"
|
||||
|
||||
#include "pika-gegl-nodes.h"
|
||||
#include "pika-gegl-utils.h"
|
||||
|
||||
|
||||
GeglNode *
|
||||
pika_gegl_create_flatten_node (const PikaRGB *background,
|
||||
const Babl *space,
|
||||
PikaLayerColorSpace composite_space)
|
||||
{
|
||||
GeglNode *node;
|
||||
GeglNode *input;
|
||||
GeglNode *output;
|
||||
GeglNode *color;
|
||||
GeglNode *mode;
|
||||
GeglColor *c;
|
||||
|
||||
g_return_val_if_fail (background != NULL, NULL);
|
||||
g_return_val_if_fail (composite_space == PIKA_LAYER_COLOR_SPACE_RGB_LINEAR ||
|
||||
composite_space == PIKA_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
|
||||
NULL);
|
||||
|
||||
node = gegl_node_new ();
|
||||
|
||||
input = gegl_node_get_input_proxy (node, "input");
|
||||
output = gegl_node_get_output_proxy (node, "output");
|
||||
|
||||
c = pika_gegl_color_new (background, space);
|
||||
color = gegl_node_new_child (node,
|
||||
"operation", "gegl:color",
|
||||
"value", c,
|
||||
"format", pika_layer_mode_get_format (
|
||||
PIKA_LAYER_MODE_NORMAL,
|
||||
PIKA_LAYER_COLOR_SPACE_AUTO,
|
||||
composite_space,
|
||||
PIKA_LAYER_COMPOSITE_AUTO,
|
||||
NULL),
|
||||
NULL);
|
||||
g_object_unref (c);
|
||||
|
||||
pika_gegl_node_set_underlying_operation (node, color);
|
||||
|
||||
mode = gegl_node_new_child (node,
|
||||
"operation", "pika:normal",
|
||||
NULL);
|
||||
pika_gegl_mode_node_set_mode (mode,
|
||||
PIKA_LAYER_MODE_NORMAL,
|
||||
PIKA_LAYER_COLOR_SPACE_AUTO,
|
||||
composite_space,
|
||||
PIKA_LAYER_COMPOSITE_AUTO);
|
||||
|
||||
gegl_node_connect (input, "output",
|
||||
mode, "aux");
|
||||
gegl_node_link_many (color, mode, output, NULL);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
GeglNode *
|
||||
pika_gegl_create_apply_opacity_node (GeglBuffer *mask,
|
||||
gint mask_offset_x,
|
||||
gint mask_offset_y,
|
||||
gdouble opacity)
|
||||
{
|
||||
GeglNode *node;
|
||||
GeglNode *input;
|
||||
GeglNode *output;
|
||||
GeglNode *opacity_node;
|
||||
GeglNode *mask_source;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (mask), NULL);
|
||||
|
||||
node = gegl_node_new ();
|
||||
|
||||
input = gegl_node_get_input_proxy (node, "input");
|
||||
output = gegl_node_get_output_proxy (node, "output");
|
||||
|
||||
opacity_node = gegl_node_new_child (node,
|
||||
"operation", "gegl:opacity",
|
||||
"value", opacity,
|
||||
NULL);
|
||||
|
||||
pika_gegl_node_set_underlying_operation (node, opacity_node);
|
||||
|
||||
mask_source = pika_gegl_add_buffer_source (node, mask,
|
||||
mask_offset_x,
|
||||
mask_offset_y);
|
||||
|
||||
gegl_node_link_many (input, opacity_node, output, NULL);
|
||||
gegl_node_connect (mask_source, "output",
|
||||
opacity_node, "aux");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
GeglNode *
|
||||
pika_gegl_create_transform_node (const PikaMatrix3 *matrix)
|
||||
{
|
||||
GeglNode *node;
|
||||
|
||||
g_return_val_if_fail (matrix != NULL, NULL);
|
||||
|
||||
node = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:transform",
|
||||
NULL);
|
||||
|
||||
pika_gegl_node_set_matrix (node, matrix);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
GeglNode *
|
||||
pika_gegl_add_buffer_source (GeglNode *parent,
|
||||
GeglBuffer *buffer,
|
||||
gint offset_x,
|
||||
gint offset_y)
|
||||
{
|
||||
GeglNode *buffer_source;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (parent), NULL);
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
|
||||
|
||||
buffer_source = gegl_node_new_child (parent,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
|
||||
if (offset_x != 0 || offset_y != 0)
|
||||
{
|
||||
GeglNode *translate =
|
||||
gegl_node_new_child (parent,
|
||||
"operation", "gegl:translate",
|
||||
"x", (gdouble) offset_x,
|
||||
"y", (gdouble) offset_y,
|
||||
NULL);
|
||||
|
||||
gegl_node_link (buffer_source, translate);
|
||||
|
||||
buffer_source = translate;
|
||||
}
|
||||
|
||||
return buffer_source;
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_mode_node_set_mode (GeglNode *node,
|
||||
PikaLayerMode mode,
|
||||
PikaLayerColorSpace blend_space,
|
||||
PikaLayerColorSpace composite_space,
|
||||
PikaLayerCompositeMode composite_mode)
|
||||
{
|
||||
gdouble opacity;
|
||||
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
|
||||
if (blend_space == PIKA_LAYER_COLOR_SPACE_AUTO)
|
||||
blend_space = pika_layer_mode_get_blend_space (mode);
|
||||
|
||||
if (composite_space == PIKA_LAYER_COLOR_SPACE_AUTO)
|
||||
composite_space = pika_layer_mode_get_composite_space (mode);
|
||||
|
||||
if (composite_mode == PIKA_LAYER_COMPOSITE_AUTO)
|
||||
composite_mode = pika_layer_mode_get_composite_mode (mode);
|
||||
|
||||
gegl_node_get (node,
|
||||
"opacity", &opacity,
|
||||
NULL);
|
||||
|
||||
/* setting the operation creates a new instance, so we have to set
|
||||
* all its properties
|
||||
*/
|
||||
gegl_node_set (node,
|
||||
"operation", pika_layer_mode_get_operation_name (mode),
|
||||
"layer-mode", mode,
|
||||
"opacity", opacity,
|
||||
"blend-space", blend_space,
|
||||
"composite-space", composite_space,
|
||||
"composite-mode", composite_mode,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_mode_node_set_opacity (GeglNode *node,
|
||||
gdouble opacity)
|
||||
{
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
|
||||
gegl_node_set (node,
|
||||
"opacity", opacity,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_node_set_matrix (GeglNode *node,
|
||||
const PikaMatrix3 *matrix)
|
||||
{
|
||||
gchar *matrix_string;
|
||||
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
g_return_if_fail (matrix != NULL);
|
||||
|
||||
matrix_string = gegl_matrix3_to_string ((GeglMatrix3 *) matrix);
|
||||
|
||||
gegl_node_set (node,
|
||||
"transform", matrix_string,
|
||||
NULL);
|
||||
|
||||
g_free (matrix_string);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_node_set_color (GeglNode *node,
|
||||
const PikaRGB *color,
|
||||
const Babl *space)
|
||||
{
|
||||
GeglColor *gegl_color;
|
||||
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
g_return_if_fail (color != NULL);
|
||||
|
||||
gegl_color = pika_gegl_color_new (color, space);
|
||||
|
||||
gegl_node_set (node,
|
||||
"value", gegl_color,
|
||||
NULL);
|
||||
|
||||
g_object_unref (gegl_color);
|
||||
}
|
58
app/gegl/pika-gegl-nodes.h
Normal file
58
app/gegl/pika-gegl-nodes.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-nodes.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_NODES_H__
|
||||
#define __PIKA_GEGL_NODES_H__
|
||||
|
||||
|
||||
GeglNode * pika_gegl_create_flatten_node (const PikaRGB *background,
|
||||
const Babl *space,
|
||||
PikaLayerColorSpace composite_space);
|
||||
GeglNode * pika_gegl_create_apply_opacity_node (GeglBuffer *mask,
|
||||
gint mask_offset_x,
|
||||
gint mask_offset_y,
|
||||
gdouble opacity);
|
||||
GeglNode * pika_gegl_create_transform_node (const PikaMatrix3 *matrix);
|
||||
|
||||
GeglNode * pika_gegl_add_buffer_source (GeglNode *parent,
|
||||
GeglBuffer *buffer,
|
||||
gint offset_x,
|
||||
gint offset_y);
|
||||
|
||||
void pika_gegl_mode_node_set_mode (GeglNode *node,
|
||||
PikaLayerMode mode,
|
||||
PikaLayerColorSpace blend_space,
|
||||
PikaLayerColorSpace composite_space,
|
||||
PikaLayerCompositeMode composite_mode);
|
||||
void pika_gegl_mode_node_set_opacity (GeglNode *node,
|
||||
gdouble opacity);
|
||||
|
||||
void pika_gegl_node_set_matrix (GeglNode *node,
|
||||
const PikaMatrix3 *matrix);
|
||||
void pika_gegl_node_set_color (GeglNode *node,
|
||||
const PikaRGB *color,
|
||||
const Babl *space);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_NODES_H__ */
|
83
app/gegl/pika-gegl-tile-compat.c
Normal file
83
app/gegl/pika-gegl-tile-compat.c
Normal file
@ -0,0 +1,83 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-tile-compat.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "pika-gegl-tile-compat.h"
|
||||
|
||||
|
||||
gint
|
||||
pika_gegl_buffer_get_n_tile_rows (GeglBuffer *buffer,
|
||||
gint tile_height)
|
||||
{
|
||||
return (gegl_buffer_get_height (buffer) + tile_height - 1) / tile_height;
|
||||
}
|
||||
|
||||
gint
|
||||
pika_gegl_buffer_get_n_tile_cols (GeglBuffer *buffer,
|
||||
gint tile_width)
|
||||
{
|
||||
return (gegl_buffer_get_width (buffer) + tile_width - 1) / tile_width;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_buffer_get_tile_rect (GeglBuffer *buffer,
|
||||
gint tile_width,
|
||||
gint tile_height,
|
||||
gint tile_num,
|
||||
GeglRectangle *rect)
|
||||
{
|
||||
gint n_tile_rows;
|
||||
gint n_tile_columns;
|
||||
gint tile_row;
|
||||
gint tile_column;
|
||||
|
||||
n_tile_rows = pika_gegl_buffer_get_n_tile_rows (buffer, tile_height);
|
||||
n_tile_columns = pika_gegl_buffer_get_n_tile_cols (buffer, tile_width);
|
||||
|
||||
if (tile_num > n_tile_rows * n_tile_columns - 1)
|
||||
return FALSE;
|
||||
|
||||
tile_row = tile_num / n_tile_columns;
|
||||
tile_column = tile_num % n_tile_columns;
|
||||
|
||||
rect->x = tile_column * tile_width;
|
||||
rect->y = tile_row * tile_height;
|
||||
|
||||
if (tile_column == n_tile_columns - 1)
|
||||
rect->width = gegl_buffer_get_width (buffer) - rect->x;
|
||||
else
|
||||
rect->width = tile_width;
|
||||
|
||||
if (tile_row == n_tile_rows - 1)
|
||||
rect->height = gegl_buffer_get_height (buffer) - rect->y;
|
||||
else
|
||||
rect->height = tile_height;
|
||||
|
||||
return TRUE;
|
||||
}
|
40
app/gegl/pika-gegl-tile-compat.h
Normal file
40
app/gegl/pika-gegl-tile-compat.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-tile-compat.h
|
||||
* Copyright (C) 2012 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_TILE_COMPAT_H__
|
||||
#define __PIKA_GEGL_TILE_COMPAT_H__
|
||||
|
||||
|
||||
gint pika_gegl_buffer_get_n_tile_rows (GeglBuffer *buffer,
|
||||
gint tile_height);
|
||||
gint pika_gegl_buffer_get_n_tile_cols (GeglBuffer *buffer,
|
||||
gint tile_width);
|
||||
gboolean pika_gegl_buffer_get_tile_rect (GeglBuffer *buffer,
|
||||
gint tile_width,
|
||||
gint tile_height,
|
||||
gint tile_num,
|
||||
GeglRectangle *rect);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_TILE_COMPAT_H__ */
|
38
app/gegl/pika-gegl-types.h
Normal file
38
app/gegl/pika-gegl-types.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-types.h
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_TYPES_H__
|
||||
#define __PIKA_GEGL_TYPES_H__
|
||||
|
||||
|
||||
#include "core/core-types.h"
|
||||
|
||||
#include "gegl/pika-gegl-enums.h"
|
||||
|
||||
#include "operations/operations-types.h"
|
||||
|
||||
|
||||
typedef struct _PikaApplicator PikaApplicator;
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_TYPES_H__ */
|
636
app/gegl/pika-gegl-utils.c
Normal file
636
app/gegl/pika-gegl-utils.c
Normal file
@ -0,0 +1,636 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-utils.h
|
||||
* Copyright (C) 2007 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gegl.h>
|
||||
#include <gegl-plugin.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "core/pikaprogress.h"
|
||||
|
||||
#include "pika-gegl-loops.h"
|
||||
#include "pika-gegl-utils.h"
|
||||
|
||||
|
||||
/* local function prototypes */
|
||||
|
||||
static gboolean pika_gegl_op_blacklisted (const gchar *name,
|
||||
const gchar *categories);
|
||||
static GList * pika_gegl_get_op_subclasses (GType type,
|
||||
GList *classes);
|
||||
static gint pika_gegl_compare_op_names (GeglOperationClass *a,
|
||||
GeglOperationClass *b);
|
||||
|
||||
|
||||
/* public functions */
|
||||
|
||||
GList *
|
||||
pika_gegl_get_op_classes (void)
|
||||
{
|
||||
GList *operations;
|
||||
|
||||
operations = pika_gegl_get_op_subclasses (GEGL_TYPE_OPERATION, NULL);
|
||||
|
||||
operations = g_list_sort (operations,
|
||||
(GCompareFunc)
|
||||
pika_gegl_compare_op_names);
|
||||
|
||||
return operations;
|
||||
}
|
||||
|
||||
GType
|
||||
pika_gegl_get_op_enum_type (const gchar *operation,
|
||||
const gchar *property)
|
||||
{
|
||||
GeglNode *node;
|
||||
GObject *op;
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_return_val_if_fail (operation != NULL, G_TYPE_NONE);
|
||||
g_return_val_if_fail (property != NULL, G_TYPE_NONE);
|
||||
|
||||
node = g_object_new (GEGL_TYPE_NODE,
|
||||
"operation", operation,
|
||||
NULL);
|
||||
g_object_get (node, "gegl-operation", &op, NULL);
|
||||
g_object_unref (node);
|
||||
|
||||
g_return_val_if_fail (op != NULL, G_TYPE_NONE);
|
||||
|
||||
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (op), property);
|
||||
|
||||
g_return_val_if_fail (G_IS_PARAM_SPEC_ENUM (pspec), G_TYPE_NONE);
|
||||
|
||||
g_object_unref (op);
|
||||
|
||||
return G_TYPE_FROM_CLASS (G_PARAM_SPEC_ENUM (pspec)->enum_class);
|
||||
}
|
||||
|
||||
GeglColor *
|
||||
pika_gegl_color_new (const PikaRGB *rgb,
|
||||
const Babl *space)
|
||||
{
|
||||
GeglColor *color;
|
||||
|
||||
g_return_val_if_fail (rgb != NULL, NULL);
|
||||
|
||||
color = gegl_color_new (NULL);
|
||||
gegl_color_set_pixel (color,
|
||||
babl_format_with_space ("R'G'B'A double", space),
|
||||
rgb);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_progress_callback (GObject *object,
|
||||
gdouble value,
|
||||
PikaProgress *progress)
|
||||
{
|
||||
if (value == 0.0)
|
||||
{
|
||||
const gchar *text = g_object_get_data (object, "pika-progress-text");
|
||||
|
||||
if (pika_progress_is_active (progress))
|
||||
pika_progress_set_text (progress, "%s", text);
|
||||
else
|
||||
pika_progress_start (progress, FALSE, "%s", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_progress_set_value (progress, value);
|
||||
|
||||
if (value == 1.0)
|
||||
pika_progress_end (progress);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_progress_connect (GeglNode *node,
|
||||
PikaProgress *progress,
|
||||
const gchar *text)
|
||||
{
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
g_return_if_fail (PIKA_IS_PROGRESS (progress));
|
||||
g_return_if_fail (text != NULL);
|
||||
|
||||
g_signal_connect (node, "progress",
|
||||
G_CALLBACK (pika_gegl_progress_callback),
|
||||
progress);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (node),
|
||||
"pika-progress-text", g_strdup (text),
|
||||
(GDestroyNotify) g_free);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_node_is_source_operation (GeglNode *node)
|
||||
{
|
||||
GeglOperation *operation;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), FALSE);
|
||||
|
||||
operation = gegl_node_get_gegl_operation (node);
|
||||
|
||||
if (! operation)
|
||||
return FALSE;
|
||||
|
||||
return GEGL_IS_OPERATION_SOURCE (operation);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_node_is_point_operation (GeglNode *node)
|
||||
{
|
||||
GeglOperation *operation;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), FALSE);
|
||||
|
||||
operation = gegl_node_get_gegl_operation (node);
|
||||
|
||||
if (! operation)
|
||||
return FALSE;
|
||||
|
||||
return GEGL_IS_OPERATION_POINT_RENDER (operation) ||
|
||||
GEGL_IS_OPERATION_POINT_FILTER (operation) ||
|
||||
GEGL_IS_OPERATION_POINT_COMPOSER (operation) ||
|
||||
GEGL_IS_OPERATION_POINT_COMPOSER3 (operation);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_node_is_area_filter_operation (GeglNode *node)
|
||||
{
|
||||
GeglOperation *operation;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), FALSE);
|
||||
|
||||
operation = gegl_node_get_gegl_operation (node);
|
||||
|
||||
if (! operation)
|
||||
return FALSE;
|
||||
|
||||
return GEGL_IS_OPERATION_AREA_FILTER (operation) ||
|
||||
/* be conservative and return TRUE for meta ops, since they may
|
||||
* involve an area op
|
||||
*/
|
||||
GEGL_IS_OPERATION_META (operation);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
pika_gegl_node_get_key (GeglNode *node,
|
||||
const gchar *key)
|
||||
{
|
||||
const gchar *operation_name;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), NULL);
|
||||
|
||||
operation_name = gegl_node_get_operation (node);
|
||||
|
||||
if (operation_name)
|
||||
return gegl_operation_get_key (operation_name, key);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_node_has_key (GeglNode *node,
|
||||
const gchar *key)
|
||||
{
|
||||
return pika_gegl_node_get_key (node, key) != NULL;
|
||||
}
|
||||
|
||||
const Babl *
|
||||
pika_gegl_node_get_format (GeglNode *node,
|
||||
const gchar *pad_name)
|
||||
{
|
||||
GeglOperation *op;
|
||||
const Babl *format = NULL;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), NULL);
|
||||
g_return_val_if_fail (pad_name != NULL, NULL);
|
||||
|
||||
g_object_get (node, "gegl-operation", &op, NULL);
|
||||
|
||||
if (op)
|
||||
{
|
||||
format = gegl_operation_get_format (op, pad_name);
|
||||
|
||||
g_object_unref (op);
|
||||
}
|
||||
|
||||
if (! format)
|
||||
format = babl_format ("RGBA float");
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_node_set_underlying_operation (GeglNode *node,
|
||||
GeglNode *operation)
|
||||
{
|
||||
g_return_if_fail (GEGL_IS_NODE (node));
|
||||
g_return_if_fail (operation == NULL || GEGL_IS_NODE (operation));
|
||||
|
||||
g_object_set_data (G_OBJECT (node),
|
||||
"pika-gegl-node-underlying-operation", operation);
|
||||
}
|
||||
|
||||
GeglNode *
|
||||
pika_gegl_node_get_underlying_operation (GeglNode *node)
|
||||
{
|
||||
GeglNode *operation;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (node), NULL);
|
||||
|
||||
operation = g_object_get_data (G_OBJECT (node),
|
||||
"pika-gegl-node-underlying-operation");
|
||||
|
||||
if (operation)
|
||||
return pika_gegl_node_get_underlying_operation (operation);
|
||||
else
|
||||
return node;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_param_spec_has_key (GParamSpec *pspec,
|
||||
const gchar *key,
|
||||
const gchar *value)
|
||||
{
|
||||
const gchar *v = gegl_param_spec_get_property_key (pspec, key);
|
||||
|
||||
if (v && ! strcmp (v, value))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GeglBuffer *
|
||||
pika_gegl_buffer_dup (GeglBuffer *buffer)
|
||||
{
|
||||
GeglBuffer *new_buffer;
|
||||
const GeglRectangle *extent;
|
||||
const GeglRectangle *abyss;
|
||||
GeglRectangle rect;
|
||||
gint shift_x;
|
||||
gint shift_y;
|
||||
gint tile_width;
|
||||
gint tile_height;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
|
||||
|
||||
extent = gegl_buffer_get_extent (buffer);
|
||||
abyss = gegl_buffer_get_abyss (buffer);
|
||||
|
||||
g_object_get (buffer,
|
||||
"shift-x", &shift_x,
|
||||
"shift-y", &shift_y,
|
||||
"tile-width", &tile_width,
|
||||
"tile-height", &tile_height,
|
||||
NULL);
|
||||
|
||||
new_buffer = g_object_new (GEGL_TYPE_BUFFER,
|
||||
"format", gegl_buffer_get_format (buffer),
|
||||
"x", extent->x,
|
||||
"y", extent->y,
|
||||
"width", extent->width,
|
||||
"height", extent->height,
|
||||
"abyss-x", abyss->x,
|
||||
"abyss-y", abyss->y,
|
||||
"abyss-width", abyss->width,
|
||||
"abyss-height", abyss->height,
|
||||
"shift-x", shift_x,
|
||||
"shift-y", shift_y,
|
||||
"tile-width", tile_width,
|
||||
"tile-height", tile_height,
|
||||
NULL);
|
||||
|
||||
gegl_rectangle_align_to_buffer (&rect, extent, buffer,
|
||||
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
|
||||
|
||||
pika_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE,
|
||||
new_buffer, &rect);
|
||||
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_gegl_buffer_set_extent (GeglBuffer *buffer,
|
||||
const GeglRectangle *extent)
|
||||
{
|
||||
GeglRectangle aligned_old_extent;
|
||||
GeglRectangle aligned_extent;
|
||||
GeglRectangle old_extent_rem;
|
||||
GeglRectangle diff_rects[4];
|
||||
gint n_diff_rects;
|
||||
gint i;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
|
||||
g_return_val_if_fail (extent != NULL, FALSE);
|
||||
|
||||
gegl_rectangle_align_to_buffer (&aligned_old_extent,
|
||||
gegl_buffer_get_extent (buffer), buffer,
|
||||
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
|
||||
gegl_rectangle_align_to_buffer (&aligned_extent,
|
||||
extent, buffer,
|
||||
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
|
||||
|
||||
n_diff_rects = gegl_rectangle_subtract (diff_rects,
|
||||
&aligned_old_extent,
|
||||
&aligned_extent);
|
||||
|
||||
for (i = 0; i < n_diff_rects; i++)
|
||||
gegl_buffer_clear (buffer, &diff_rects[i]);
|
||||
|
||||
if (gegl_rectangle_intersect (&old_extent_rem,
|
||||
gegl_buffer_get_extent (buffer),
|
||||
&aligned_extent))
|
||||
{
|
||||
n_diff_rects = gegl_rectangle_subtract (diff_rects,
|
||||
&old_extent_rem,
|
||||
extent);
|
||||
|
||||
for (i = 0; i < n_diff_rects; i++)
|
||||
gegl_buffer_clear (buffer, &diff_rects[i]);
|
||||
}
|
||||
|
||||
return gegl_buffer_set_extent (buffer, extent);
|
||||
}
|
||||
|
||||
|
||||
/* private functions */
|
||||
|
||||
static gboolean
|
||||
pika_gegl_op_blacklisted (const gchar *name,
|
||||
const gchar *categories_str)
|
||||
{
|
||||
static const gchar * const category_blacklist[] =
|
||||
{
|
||||
"compositors",
|
||||
"core",
|
||||
"debug",
|
||||
"display",
|
||||
"hidden",
|
||||
"input",
|
||||
"output",
|
||||
"programming",
|
||||
"transform",
|
||||
"video"
|
||||
};
|
||||
static const gchar * const name_blacklist[] =
|
||||
{
|
||||
/* these ops are already added to the menus via filters-actions */
|
||||
"gegl:alien-map",
|
||||
"gegl:antialias",
|
||||
"gegl:apply-lens",
|
||||
"gegl:bayer-matrix",
|
||||
"gegl:bloom",
|
||||
"gegl:bump-map",
|
||||
"gegl:c2g",
|
||||
"gegl:cartoon",
|
||||
"gegl:cell-noise",
|
||||
"gegl:channel-mixer",
|
||||
"gegl:checkerboard",
|
||||
"gegl:color",
|
||||
"gegl:color-enhance",
|
||||
"gegl:color-exchange",
|
||||
"gegl:color-rotate",
|
||||
"gegl:color-temperature",
|
||||
"gegl:color-to-alpha",
|
||||
"gegl:component-extract",
|
||||
"gegl:convolution-matrix",
|
||||
"gegl:cubism",
|
||||
"gegl:deinterlace",
|
||||
"gegl:difference-of-gaussians",
|
||||
"gegl:diffraction-patterns",
|
||||
"gegl:displace",
|
||||
"gegl:distance-transform",
|
||||
"gegl:dither",
|
||||
"gegl:dropshadow",
|
||||
"gegl:edge",
|
||||
"gegl:edge-laplace",
|
||||
"gegl:edge-neon",
|
||||
"gegl:edge-sobel",
|
||||
"gegl:emboss",
|
||||
"gegl:engrave",
|
||||
"gegl:exposure",
|
||||
"gegl:fattal02",
|
||||
"gegl:focus-blur",
|
||||
"gegl:fractal-trace",
|
||||
"gegl:gaussian-blur",
|
||||
"gegl:gaussian-blur-selective",
|
||||
"gegl:gegl",
|
||||
"gegl:grid",
|
||||
"gegl:high-pass",
|
||||
"gegl:hue-chroma",
|
||||
"gegl:illusion",
|
||||
"gegl:image-gradient",
|
||||
"gegl:invert-linear",
|
||||
"gegl:invert-gamma",
|
||||
"gegl:lens-blur",
|
||||
"gegl:lens-distortion",
|
||||
"gegl:lens-flare",
|
||||
"gegl:linear-sinusoid",
|
||||
"gegl:long-shadow",
|
||||
"gegl:mantiuk06",
|
||||
"gegl:maze",
|
||||
"gegl:mean-curvature-blur",
|
||||
"gegl:median-blur",
|
||||
"gegl:mirrors",
|
||||
"gegl:mono-mixer",
|
||||
"gegl:mosaic",
|
||||
"gegl:motion-blur-circular",
|
||||
"gegl:motion-blur-linear",
|
||||
"gegl:motion-blur-zoom",
|
||||
"gegl:newsprint",
|
||||
"gegl:noise-cie-lch",
|
||||
"gegl:noise-hsv",
|
||||
"gegl:noise-hurl",
|
||||
"gegl:noise-pick",
|
||||
"gegl:noise-reduction",
|
||||
"gegl:noise-rgb",
|
||||
"gegl:noise-slur",
|
||||
"gegl:noise-solid",
|
||||
"gegl:noise-spread",
|
||||
"gegl:normal-map",
|
||||
"gegl:oilify",
|
||||
"gegl:panorama-projection",
|
||||
"gegl:perlin-noise",
|
||||
"gegl:photocopy",
|
||||
"gegl:pixelize",
|
||||
"gegl:plasma",
|
||||
"gegl:polar-coordinates",
|
||||
"gegl:recursive-transform",
|
||||
"gegl:red-eye-removal",
|
||||
"gegl:reinhard05",
|
||||
"gegl:rgb-clip",
|
||||
"gegl:ripple",
|
||||
"gegl:saturation",
|
||||
"gegl:sepia",
|
||||
"gegl:shadows-highlights",
|
||||
"gegl:shift",
|
||||
"gegl:simplex-noise",
|
||||
"gegl:sinus",
|
||||
"gegl:slic",
|
||||
"gegl:snn-mean",
|
||||
"gegl:softglow",
|
||||
"gegl:spherize",
|
||||
"gegl:spiral",
|
||||
"gegl:stereographic-projection",
|
||||
"gegl:stretch-contrast",
|
||||
"gegl:stretch-contrast-hsv",
|
||||
"gegl:stress",
|
||||
"gegl:supernova",
|
||||
"gegl:texturize-canvas",
|
||||
"gegl:tile-glass",
|
||||
"gegl:tile-paper",
|
||||
"gegl:tile-seamless",
|
||||
"gegl:unsharp-mask",
|
||||
"gegl:value-invert",
|
||||
"gegl:value-propagate",
|
||||
"gegl:variable-blur",
|
||||
"gegl:video-degradation",
|
||||
"gegl:vignette",
|
||||
"gegl:waterpixels",
|
||||
"gegl:wavelet-blur",
|
||||
"gegl:waves",
|
||||
"gegl:whirl-pinch",
|
||||
"gegl:wind",
|
||||
|
||||
/* these ops are blacklisted for other reasons */
|
||||
"gegl:contrast-curve",
|
||||
"gegl:convert-format", /* pointless */
|
||||
"gegl:ditto", /* pointless */
|
||||
"gegl:fill-path",
|
||||
"gegl:gray", /* we use pika's op */
|
||||
"gegl:hstack", /* pointless */
|
||||
"gegl:introspect", /* pointless */
|
||||
"gegl:layer", /* we use pika's ops */
|
||||
"gegl:lcms-from-profile", /* not usable here */
|
||||
"gegl:linear-gradient", /* we use the blend tool */
|
||||
"gegl:map-absolute", /* pointless */
|
||||
"gegl:map-relative", /* pointless */
|
||||
"gegl:matting-global", /* used in the foreground select tool */
|
||||
"gegl:matting-levin", /* used in the foreground select tool */
|
||||
"gegl:opacity", /* poinless */
|
||||
"gegl:path",
|
||||
"gegl:posterize", /* we use pika's op */
|
||||
"gegl:radial-gradient", /* we use the blend tool */
|
||||
"gegl:rectangle", /* pointless */
|
||||
"gegl:seamless-clone", /* used in the seamless clone tool */
|
||||
"gegl:text", /* we use pika's text rendering */
|
||||
"gegl:threshold", /* we use pika's op */
|
||||
"gegl:tile", /* pointless */
|
||||
"gegl:unpremul", /* pointless */
|
||||
"gegl:vector-stroke",
|
||||
};
|
||||
|
||||
gchar **categories;
|
||||
gint i;
|
||||
|
||||
/* Operations with no name are abstract base classes */
|
||||
if (! name)
|
||||
return TRUE;
|
||||
|
||||
/* use this flag to include all ops for testing */
|
||||
if (g_getenv ("PIKA_TESTING_NO_GEGL_BLACKLIST"))
|
||||
return FALSE;
|
||||
|
||||
if (g_str_has_prefix (name, "pika"))
|
||||
return TRUE;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (name_blacklist); i++)
|
||||
{
|
||||
if (! strcmp (name, name_blacklist[i]))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (! categories_str)
|
||||
return FALSE;
|
||||
|
||||
categories = g_strsplit (categories_str, ":", 0);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (category_blacklist); i++)
|
||||
{
|
||||
gint j;
|
||||
|
||||
for (j = 0; categories[j]; j++)
|
||||
if (! strcmp (categories[j], category_blacklist[i]))
|
||||
{
|
||||
g_strfreev (categories);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (categories);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Builds a GList of the class structures of all subtypes of type.
|
||||
*/
|
||||
static GList *
|
||||
pika_gegl_get_op_subclasses (GType type,
|
||||
GList *classes)
|
||||
{
|
||||
GeglOperationClass *klass;
|
||||
GType *ops;
|
||||
const gchar *categories;
|
||||
guint n_ops;
|
||||
gint i;
|
||||
|
||||
if (! type)
|
||||
return classes;
|
||||
|
||||
klass = GEGL_OPERATION_CLASS (g_type_class_ref (type));
|
||||
ops = g_type_children (type, &n_ops);
|
||||
|
||||
categories = gegl_operation_class_get_key (klass, "categories");
|
||||
|
||||
if (! pika_gegl_op_blacklisted (klass->name, categories))
|
||||
classes = g_list_prepend (classes, klass);
|
||||
|
||||
for (i = 0; i < n_ops; i++)
|
||||
classes = pika_gegl_get_op_subclasses (ops[i], classes);
|
||||
|
||||
if (ops)
|
||||
g_free (ops);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
static gint
|
||||
pika_gegl_compare_op_names (GeglOperationClass *a,
|
||||
GeglOperationClass *b)
|
||||
{
|
||||
const gchar *name_a = gegl_operation_class_get_key (a, "title");
|
||||
const gchar *name_b = gegl_operation_class_get_key (b, "title");
|
||||
|
||||
if (! name_a) name_a = a->name;
|
||||
if (! name_b) name_b = b->name;
|
||||
|
||||
return strcmp (name_a, name_b);
|
||||
}
|
67
app/gegl/pika-gegl-utils.h
Normal file
67
app/gegl/pika-gegl-utils.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl-utils.h
|
||||
* Copyright (C) 2007 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_UTILS_H__
|
||||
#define __PIKA_GEGL_UTILS_H__
|
||||
|
||||
|
||||
GList * pika_gegl_get_op_classes (void);
|
||||
|
||||
GType pika_gegl_get_op_enum_type (const gchar *operation,
|
||||
const gchar *property);
|
||||
|
||||
GeglColor * pika_gegl_color_new (const PikaRGB *rgb,
|
||||
const Babl *space);
|
||||
|
||||
void pika_gegl_progress_connect (GeglNode *node,
|
||||
PikaProgress *progress,
|
||||
const gchar *text);
|
||||
|
||||
gboolean pika_gegl_node_is_source_operation (GeglNode *node);
|
||||
gboolean pika_gegl_node_is_point_operation (GeglNode *node);
|
||||
gboolean pika_gegl_node_is_area_filter_operation (GeglNode *node);
|
||||
|
||||
const gchar * pika_gegl_node_get_key (GeglNode *node,
|
||||
const gchar *key);
|
||||
gboolean pika_gegl_node_has_key (GeglNode *node,
|
||||
const gchar *key);
|
||||
|
||||
const Babl * pika_gegl_node_get_format (GeglNode *node,
|
||||
const gchar *pad_name);
|
||||
|
||||
void pika_gegl_node_set_underlying_operation (GeglNode *node,
|
||||
GeglNode *operation);
|
||||
GeglNode * pika_gegl_node_get_underlying_operation (GeglNode *node);
|
||||
|
||||
gboolean pika_gegl_param_spec_has_key (GParamSpec *pspec,
|
||||
const gchar *key,
|
||||
const gchar *value);
|
||||
|
||||
GeglBuffer * pika_gegl_buffer_dup (GeglBuffer *buffer);
|
||||
|
||||
gboolean pika_gegl_buffer_set_extent (GeglBuffer *buffer,
|
||||
const GeglRectangle *extent);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_UTILS_H__ */
|
176
app/gegl/pika-gegl.c
Normal file
176
app/gegl/pika-gegl.c
Normal file
@ -0,0 +1,176 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl.c
|
||||
* Copyright (C) 2007 Øyvind Kolås <pippin@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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "config/pikageglconfig.h"
|
||||
|
||||
#include "operations/pika-operations.h"
|
||||
|
||||
#include "core/pika.h"
|
||||
#include "core/pika-parallel.h"
|
||||
|
||||
#include "pika-babl.h"
|
||||
#include "pika-gegl.h"
|
||||
|
||||
#include <operation/gegl-operation.h>
|
||||
|
||||
|
||||
static void pika_gegl_notify_temp_path (PikaGeglConfig *config);
|
||||
static void pika_gegl_notify_swap_path (PikaGeglConfig *config);
|
||||
static void pika_gegl_notify_swap_compression (PikaGeglConfig *config);
|
||||
static void pika_gegl_notify_tile_cache_size (PikaGeglConfig *config);
|
||||
static void pika_gegl_notify_num_processors (PikaGeglConfig *config);
|
||||
static void pika_gegl_notify_use_opencl (PikaGeglConfig *config);
|
||||
|
||||
|
||||
/* public functions */
|
||||
|
||||
void
|
||||
pika_gegl_init (Pika *pika)
|
||||
{
|
||||
PikaGeglConfig *config;
|
||||
|
||||
g_return_if_fail (PIKA_IS_PIKA (pika));
|
||||
|
||||
config = PIKA_GEGL_CONFIG (pika->config);
|
||||
|
||||
/* make sure temp and swap directories exist */
|
||||
pika_gegl_notify_temp_path (config);
|
||||
pika_gegl_notify_swap_path (config);
|
||||
|
||||
g_object_set (gegl_config (),
|
||||
"swap-compression", config->swap_compression,
|
||||
"tile-cache-size", (guint64) config->tile_cache_size,
|
||||
"threads", config->num_processors,
|
||||
"use-opencl", config->use_opencl,
|
||||
NULL);
|
||||
|
||||
pika_parallel_init (pika);
|
||||
|
||||
g_signal_connect (config, "notify::temp-path",
|
||||
G_CALLBACK (pika_gegl_notify_temp_path),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::swap-path",
|
||||
G_CALLBACK (pika_gegl_notify_swap_path),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::swap-compression",
|
||||
G_CALLBACK (pika_gegl_notify_swap_compression),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::num-processors",
|
||||
G_CALLBACK (pika_gegl_notify_num_processors),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::tile-cache-size",
|
||||
G_CALLBACK (pika_gegl_notify_tile_cache_size),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::num-processors",
|
||||
G_CALLBACK (pika_gegl_notify_num_processors),
|
||||
NULL);
|
||||
g_signal_connect (config, "notify::use-opencl",
|
||||
G_CALLBACK (pika_gegl_notify_use_opencl),
|
||||
NULL);
|
||||
|
||||
pika_babl_init ();
|
||||
|
||||
pika_operations_init (pika);
|
||||
}
|
||||
|
||||
void
|
||||
pika_gegl_exit (Pika *pika)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_PIKA (pika));
|
||||
|
||||
pika_operations_exit (pika);
|
||||
pika_parallel_exit (pika);
|
||||
}
|
||||
|
||||
|
||||
/* private functions */
|
||||
|
||||
static void
|
||||
pika_gegl_notify_temp_path (PikaGeglConfig *config)
|
||||
{
|
||||
GFile *file = pika_file_new_for_config_path (config->temp_path, NULL);
|
||||
|
||||
if (! g_file_query_exists (file, NULL))
|
||||
g_file_make_directory_with_parents (file, NULL, NULL);
|
||||
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_notify_swap_path (PikaGeglConfig *config)
|
||||
{
|
||||
GFile *file = pika_file_new_for_config_path (config->swap_path, NULL);
|
||||
gchar *path = g_file_get_path (file);
|
||||
|
||||
if (! g_file_query_exists (file, NULL))
|
||||
g_file_make_directory_with_parents (file, NULL, NULL);
|
||||
|
||||
g_object_set (gegl_config (),
|
||||
"swap", path,
|
||||
NULL);
|
||||
|
||||
g_free (path);
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_notify_swap_compression (PikaGeglConfig *config)
|
||||
{
|
||||
g_object_set (gegl_config (),
|
||||
"swap-compression", config->swap_compression,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_notify_tile_cache_size (PikaGeglConfig *config)
|
||||
{
|
||||
g_object_set (gegl_config (),
|
||||
"tile-cache-size", (guint64) config->tile_cache_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_notify_num_processors (PikaGeglConfig *config)
|
||||
{
|
||||
g_object_set (gegl_config (),
|
||||
"threads", config->num_processors,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_gegl_notify_use_opencl (PikaGeglConfig *config)
|
||||
{
|
||||
g_object_set (gegl_config (),
|
||||
"use-opencl", config->use_opencl,
|
||||
NULL);
|
||||
}
|
33
app/gegl/pika-gegl.h
Normal file
33
app/gegl/pika-gegl.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pika-gegl.h
|
||||
* Copyright (C) 2007 Øyvind Kolås <pippin@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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_GEGL_H__
|
||||
#define __PIKA_GEGL_H__
|
||||
|
||||
|
||||
void pika_gegl_init (Pika *pika);
|
||||
void pika_gegl_exit (Pika *pika);
|
||||
|
||||
|
||||
#endif /* __PIKA_GEGL_H__ */
|
653
app/gegl/pikaapplicator.c
Normal file
653
app/gegl/pikaapplicator.c
Normal file
@ -0,0 +1,653 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pikaapplicator.c
|
||||
* Copyright (C) 2012-2013 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "pika-gegl-nodes.h"
|
||||
#include "pikaapplicator.h"
|
||||
|
||||
|
||||
static void pika_applicator_finalize (GObject *object);
|
||||
static void pika_applicator_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void pika_applicator_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (PikaApplicator, pika_applicator, G_TYPE_OBJECT)
|
||||
|
||||
#define parent_class pika_applicator_parent_class
|
||||
|
||||
|
||||
static void
|
||||
pika_applicator_class_init (PikaApplicatorClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = pika_applicator_finalize;
|
||||
object_class->set_property = pika_applicator_set_property;
|
||||
object_class->get_property = pika_applicator_get_property;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_applicator_init (PikaApplicator *applicator)
|
||||
{
|
||||
applicator->active = TRUE;
|
||||
applicator->opacity = 1.0;
|
||||
applicator->paint_mode = PIKA_LAYER_MODE_NORMAL;
|
||||
applicator->blend_space = PIKA_LAYER_COLOR_SPACE_AUTO;
|
||||
applicator->composite_space = PIKA_LAYER_COLOR_SPACE_AUTO;
|
||||
applicator->composite_mode = PIKA_LAYER_COMPOSITE_AUTO;
|
||||
applicator->affect = PIKA_COMPONENT_MASK_ALL;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_applicator_finalize (GObject *object)
|
||||
{
|
||||
PikaApplicator *applicator = PIKA_APPLICATOR (object);
|
||||
|
||||
g_clear_object (&applicator->node);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_applicator_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pika_applicator_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PikaApplicator *
|
||||
pika_applicator_new (GeglNode *parent)
|
||||
{
|
||||
PikaApplicator *applicator;
|
||||
|
||||
g_return_val_if_fail (parent == NULL || GEGL_IS_NODE (parent), NULL);
|
||||
|
||||
applicator = g_object_new (PIKA_TYPE_APPLICATOR, NULL);
|
||||
|
||||
if (parent)
|
||||
applicator->node = g_object_ref (parent);
|
||||
else
|
||||
applicator->node = gegl_node_new ();
|
||||
|
||||
applicator->input_node =
|
||||
gegl_node_get_input_proxy (applicator->node, "input");
|
||||
|
||||
applicator->aux_node =
|
||||
gegl_node_get_input_proxy (applicator->node, "aux");
|
||||
|
||||
applicator->output_node =
|
||||
gegl_node_get_output_proxy (applicator->node, "output");
|
||||
|
||||
applicator->mode_node = gegl_node_new_child (applicator->node,
|
||||
"operation", "pika:normal",
|
||||
NULL);
|
||||
|
||||
pika_gegl_mode_node_set_mode (applicator->mode_node,
|
||||
applicator->paint_mode,
|
||||
applicator->blend_space,
|
||||
applicator->composite_space,
|
||||
applicator->composite_mode);
|
||||
pika_gegl_mode_node_set_opacity (applicator->mode_node,
|
||||
applicator->opacity);
|
||||
|
||||
gegl_node_link (applicator->input_node, applicator->mode_node);
|
||||
|
||||
applicator->apply_offset_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:translate",
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (applicator->aux_node,
|
||||
applicator->apply_offset_node,
|
||||
NULL);
|
||||
|
||||
gegl_node_connect (applicator->apply_offset_node, "output",
|
||||
applicator->mode_node, "aux");
|
||||
|
||||
applicator->mask_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:buffer-source",
|
||||
NULL);
|
||||
|
||||
applicator->mask_offset_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:translate",
|
||||
NULL);
|
||||
|
||||
gegl_node_link (applicator->mask_node, applicator->mask_offset_node);
|
||||
/* don't connect the the mask offset node to mode's aux2 yet */
|
||||
|
||||
applicator->affect_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "pika:mask-components",
|
||||
"mask", applicator->affect,
|
||||
NULL);
|
||||
|
||||
applicator->convert_format_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
|
||||
applicator->cache_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
|
||||
applicator->crop_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (applicator->input_node,
|
||||
applicator->affect_node,
|
||||
applicator->convert_format_node,
|
||||
applicator->cache_node,
|
||||
applicator->crop_node,
|
||||
applicator->output_node,
|
||||
NULL);
|
||||
|
||||
gegl_node_connect (applicator->mode_node, "output",
|
||||
applicator->affect_node, "aux");
|
||||
|
||||
return applicator;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_active (PikaApplicator *applicator,
|
||||
gboolean active)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (active != applicator->active)
|
||||
{
|
||||
applicator->active = active;
|
||||
|
||||
if (active)
|
||||
gegl_node_link (applicator->crop_node, applicator->output_node);
|
||||
else
|
||||
gegl_node_link (applicator->input_node, applicator->output_node);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_src_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *src_buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
g_return_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer));
|
||||
|
||||
if (src_buffer == applicator->src_buffer)
|
||||
return;
|
||||
|
||||
if (src_buffer)
|
||||
{
|
||||
if (! applicator->src_node)
|
||||
{
|
||||
applicator->src_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", src_buffer,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->src_node,
|
||||
"buffer", src_buffer,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (! applicator->src_buffer)
|
||||
gegl_node_link (applicator->src_node, applicator->input_node);
|
||||
}
|
||||
else if (applicator->src_buffer)
|
||||
{
|
||||
gegl_node_disconnect (applicator->input_node, "input");
|
||||
|
||||
gegl_node_set (applicator->src_node,
|
||||
"buffer", NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
applicator->src_buffer = src_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_dest_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *dest_buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
g_return_if_fail (dest_buffer == NULL || GEGL_IS_BUFFER (dest_buffer));
|
||||
|
||||
if (dest_buffer == applicator->dest_buffer)
|
||||
return;
|
||||
|
||||
if (dest_buffer)
|
||||
{
|
||||
if (! applicator->dest_node)
|
||||
{
|
||||
applicator->dest_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", dest_buffer,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->dest_node,
|
||||
"buffer", dest_buffer,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (! applicator->dest_buffer)
|
||||
gegl_node_link (applicator->affect_node, applicator->dest_node);
|
||||
}
|
||||
else if (applicator->dest_buffer)
|
||||
{
|
||||
gegl_node_disconnect (applicator->dest_node, "input");
|
||||
|
||||
gegl_node_set (applicator->dest_node,
|
||||
"buffer", NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
applicator->dest_buffer = dest_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_mask_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *mask_buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
g_return_if_fail (mask_buffer == NULL || GEGL_IS_BUFFER (mask_buffer));
|
||||
|
||||
if (applicator->mask_buffer == mask_buffer)
|
||||
return;
|
||||
|
||||
gegl_node_set (applicator->mask_node,
|
||||
"buffer", mask_buffer,
|
||||
NULL);
|
||||
|
||||
if (mask_buffer)
|
||||
{
|
||||
gegl_node_connect (applicator->mask_offset_node, "output",
|
||||
applicator->mode_node, "aux2");
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_disconnect (applicator->mode_node, "aux2");
|
||||
}
|
||||
|
||||
applicator->mask_buffer = mask_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_mask_offset (PikaApplicator *applicator,
|
||||
gint mask_offset_x,
|
||||
gint mask_offset_y)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->mask_offset_x != mask_offset_x ||
|
||||
applicator->mask_offset_y != mask_offset_y)
|
||||
{
|
||||
applicator->mask_offset_x = mask_offset_x;
|
||||
applicator->mask_offset_y = mask_offset_y;
|
||||
|
||||
gegl_node_set (applicator->mask_offset_node,
|
||||
"x", (gdouble) mask_offset_x,
|
||||
"y", (gdouble) mask_offset_y,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_apply_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *apply_buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
g_return_if_fail (apply_buffer == NULL || GEGL_IS_BUFFER (apply_buffer));
|
||||
|
||||
if (apply_buffer == applicator->apply_buffer)
|
||||
return;
|
||||
|
||||
if (apply_buffer)
|
||||
{
|
||||
if (! applicator->apply_src_node)
|
||||
{
|
||||
applicator->apply_src_node =
|
||||
gegl_node_new_child (applicator->node,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", apply_buffer,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->apply_src_node,
|
||||
"buffer", apply_buffer,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (! applicator->apply_buffer)
|
||||
{
|
||||
gegl_node_connect (applicator->apply_src_node, "output",
|
||||
applicator->apply_offset_node, "input");
|
||||
}
|
||||
}
|
||||
else if (applicator->apply_buffer)
|
||||
{
|
||||
gegl_node_link (applicator->aux_node, applicator->apply_offset_node);
|
||||
}
|
||||
|
||||
applicator->apply_buffer = apply_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_apply_offset (PikaApplicator *applicator,
|
||||
gint apply_offset_x,
|
||||
gint apply_offset_y)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->apply_offset_x != apply_offset_x ||
|
||||
applicator->apply_offset_y != apply_offset_y)
|
||||
{
|
||||
applicator->apply_offset_x = apply_offset_x;
|
||||
applicator->apply_offset_y = apply_offset_y;
|
||||
|
||||
gegl_node_set (applicator->apply_offset_node,
|
||||
"x", (gdouble) apply_offset_x,
|
||||
"y", (gdouble) apply_offset_y,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_opacity (PikaApplicator *applicator,
|
||||
gdouble opacity)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->opacity != opacity)
|
||||
{
|
||||
applicator->opacity = opacity;
|
||||
|
||||
pika_gegl_mode_node_set_opacity (applicator->mode_node,
|
||||
opacity);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_mode (PikaApplicator *applicator,
|
||||
PikaLayerMode paint_mode,
|
||||
PikaLayerColorSpace blend_space,
|
||||
PikaLayerColorSpace composite_space,
|
||||
PikaLayerCompositeMode composite_mode)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->paint_mode != paint_mode ||
|
||||
applicator->blend_space != blend_space ||
|
||||
applicator->composite_space != composite_space ||
|
||||
applicator->composite_mode != composite_mode)
|
||||
{
|
||||
applicator->paint_mode = paint_mode;
|
||||
applicator->blend_space = blend_space;
|
||||
applicator->composite_space = composite_space;
|
||||
applicator->composite_mode = composite_mode;
|
||||
|
||||
pika_gegl_mode_node_set_mode (applicator->mode_node,
|
||||
paint_mode, blend_space,
|
||||
composite_space, composite_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_affect (PikaApplicator *applicator,
|
||||
PikaComponentMask affect)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->affect != affect)
|
||||
{
|
||||
applicator->affect = affect;
|
||||
|
||||
gegl_node_set (applicator->affect_node,
|
||||
"mask", affect,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_output_format (PikaApplicator *applicator,
|
||||
const Babl *format)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->output_format != format)
|
||||
{
|
||||
if (format)
|
||||
{
|
||||
if (! applicator->output_format)
|
||||
{
|
||||
gegl_node_set (applicator->convert_format_node,
|
||||
"operation", "gegl:convert-format",
|
||||
"format", format,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->convert_format_node,
|
||||
"format", format,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->convert_format_node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
}
|
||||
|
||||
applicator->output_format = format;
|
||||
}
|
||||
}
|
||||
|
||||
const Babl *
|
||||
pika_applicator_get_output_format (PikaApplicator *applicator)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_APPLICATOR (applicator), NULL);
|
||||
|
||||
return applicator->output_format;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_cache (PikaApplicator *applicator,
|
||||
gboolean enable)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->cache_enabled != enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
gegl_node_set (applicator->cache_node,
|
||||
"operation", "gegl:cache",
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->cache_node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
}
|
||||
|
||||
applicator->cache_enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_applicator_get_cache (PikaApplicator *applicator)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_APPLICATOR (applicator), FALSE);
|
||||
|
||||
return applicator->cache_enabled;
|
||||
}
|
||||
|
||||
gboolean gegl_buffer_list_valid_rectangles (GeglBuffer *buffer,
|
||||
GeglRectangle **rectangles,
|
||||
gint *n_rectangles);
|
||||
|
||||
GeglBuffer *
|
||||
pika_applicator_get_cache_buffer (PikaApplicator *applicator,
|
||||
GeglRectangle **rectangles,
|
||||
gint *n_rectangles)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_APPLICATOR (applicator), NULL);
|
||||
g_return_val_if_fail (rectangles != NULL, NULL);
|
||||
g_return_val_if_fail (n_rectangles != NULL, NULL);
|
||||
|
||||
if (applicator->cache_enabled)
|
||||
{
|
||||
GeglBuffer *cache;
|
||||
|
||||
gegl_node_get (applicator->cache_node,
|
||||
"cache", &cache,
|
||||
NULL);
|
||||
|
||||
if (cache)
|
||||
{
|
||||
if (gegl_buffer_list_valid_rectangles (cache,
|
||||
rectangles, n_rectangles))
|
||||
{
|
||||
return cache;
|
||||
}
|
||||
|
||||
g_object_unref (cache);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_set_crop (PikaApplicator *applicator,
|
||||
const GeglRectangle *rect)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
if (applicator->crop_enabled != (rect != NULL) ||
|
||||
(rect && ! gegl_rectangle_equal (&applicator->crop_rect, rect)))
|
||||
{
|
||||
if (rect)
|
||||
{
|
||||
if (! applicator->crop_enabled)
|
||||
{
|
||||
gegl_node_set (applicator->crop_node,
|
||||
"operation", "pika:compose-crop",
|
||||
"x", rect->x,
|
||||
"y", rect->y,
|
||||
"width", rect->width,
|
||||
"height", rect->height,
|
||||
NULL);
|
||||
|
||||
gegl_node_connect (applicator->input_node, "output",
|
||||
applicator->crop_node, "aux");
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_set (applicator->crop_node,
|
||||
"x", rect->x,
|
||||
"y", rect->y,
|
||||
"width", rect->width,
|
||||
"height", rect->height,
|
||||
NULL);
|
||||
}
|
||||
|
||||
applicator->crop_enabled = TRUE;
|
||||
applicator->crop_rect = *rect;
|
||||
}
|
||||
else
|
||||
{
|
||||
gegl_node_disconnect (applicator->crop_node, "aux");
|
||||
gegl_node_set (applicator->crop_node,
|
||||
"operation", "gegl:nop",
|
||||
NULL);
|
||||
|
||||
applicator->crop_enabled = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GeglRectangle *
|
||||
pika_applicator_get_crop (PikaApplicator *applicator)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_APPLICATOR (applicator), NULL);
|
||||
|
||||
if (applicator->crop_enabled)
|
||||
return &applicator->crop_rect;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
pika_applicator_blit (PikaApplicator *applicator,
|
||||
const GeglRectangle *rect)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_APPLICATOR (applicator));
|
||||
|
||||
gegl_node_blit (applicator->dest_node, 1.0, rect,
|
||||
NULL, NULL, 0, GEGL_BLIT_DEFAULT);
|
||||
}
|
150
app/gegl/pikaapplicator.h
Normal file
150
app/gegl/pikaapplicator.h
Normal file
@ -0,0 +1,150 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pikaapplicator.h
|
||||
* Copyright (C) 2012-2013 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
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_APPLICATOR_H__
|
||||
#define __PIKA_APPLICATOR_H__
|
||||
|
||||
|
||||
#define PIKA_TYPE_APPLICATOR (pika_applicator_get_type ())
|
||||
#define PIKA_APPLICATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_APPLICATOR, PikaApplicator))
|
||||
#define PIKA_APPLICATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_APPLICATOR, PikaApplicatorClass))
|
||||
#define PIKA_IS_APPLICATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_APPLICATOR))
|
||||
#define PIKA_IS_APPLICATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_APPLICATOR))
|
||||
#define PIKA_APPLICATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_APPLICATOR, PikaApplicatorClass))
|
||||
|
||||
|
||||
typedef struct _PikaApplicatorClass PikaApplicatorClass;
|
||||
|
||||
struct _PikaApplicator
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GeglNode *node;
|
||||
GeglNode *input_node;
|
||||
GeglNode *aux_node;
|
||||
GeglNode *output_node;
|
||||
|
||||
gboolean active;
|
||||
|
||||
GeglBuffer *apply_buffer;
|
||||
GeglNode *apply_src_node;
|
||||
|
||||
gint apply_offset_x;
|
||||
gint apply_offset_y;
|
||||
GeglNode *apply_offset_node;
|
||||
|
||||
gdouble opacity;
|
||||
PikaLayerMode paint_mode;
|
||||
PikaLayerColorSpace blend_space;
|
||||
PikaLayerColorSpace composite_space;
|
||||
PikaLayerCompositeMode composite_mode;
|
||||
GeglNode *mode_node;
|
||||
|
||||
PikaComponentMask affect;
|
||||
GeglNode *affect_node;
|
||||
|
||||
const Babl *output_format;
|
||||
GeglNode *convert_format_node;
|
||||
|
||||
gboolean cache_enabled;
|
||||
GeglNode *cache_node;
|
||||
|
||||
gboolean crop_enabled;
|
||||
GeglRectangle crop_rect;
|
||||
GeglNode *crop_node;
|
||||
|
||||
GeglBuffer *src_buffer;
|
||||
GeglNode *src_node;
|
||||
|
||||
GeglBuffer *dest_buffer;
|
||||
GeglNode *dest_node;
|
||||
|
||||
GeglBuffer *mask_buffer;
|
||||
GeglNode *mask_node;
|
||||
|
||||
gint mask_offset_x;
|
||||
gint mask_offset_y;
|
||||
GeglNode *mask_offset_node;
|
||||
};
|
||||
|
||||
struct _PikaApplicatorClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType pika_applicator_get_type (void) G_GNUC_CONST;
|
||||
|
||||
PikaApplicator * pika_applicator_new (GeglNode *parent);
|
||||
|
||||
void pika_applicator_set_active (PikaApplicator *applicator,
|
||||
gboolean active);
|
||||
|
||||
void pika_applicator_set_src_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *dest_buffer);
|
||||
void pika_applicator_set_dest_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *dest_buffer);
|
||||
|
||||
void pika_applicator_set_mask_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *mask_buffer);
|
||||
void pika_applicator_set_mask_offset (PikaApplicator *applicator,
|
||||
gint mask_offset_x,
|
||||
gint mask_offset_y);
|
||||
|
||||
void pika_applicator_set_apply_buffer (PikaApplicator *applicator,
|
||||
GeglBuffer *apply_buffer);
|
||||
void pika_applicator_set_apply_offset (PikaApplicator *applicator,
|
||||
gint apply_offset_x,
|
||||
gint apply_offset_y);
|
||||
|
||||
void pika_applicator_set_opacity (PikaApplicator *applicator,
|
||||
gdouble opacity);
|
||||
void pika_applicator_set_mode (PikaApplicator *applicator,
|
||||
PikaLayerMode paint_mode,
|
||||
PikaLayerColorSpace blend_space,
|
||||
PikaLayerColorSpace composite_space,
|
||||
PikaLayerCompositeMode composite_mode);
|
||||
void pika_applicator_set_affect (PikaApplicator *applicator,
|
||||
PikaComponentMask affect);
|
||||
|
||||
void pika_applicator_set_output_format (PikaApplicator *applicator,
|
||||
const Babl *format);
|
||||
const Babl * pika_applicator_get_output_format (PikaApplicator *applicator);
|
||||
|
||||
void pika_applicator_set_cache (PikaApplicator *applicator,
|
||||
gboolean enable);
|
||||
gboolean pika_applicator_get_cache (PikaApplicator *applicator);
|
||||
GeglBuffer * pika_applicator_get_cache_buffer (PikaApplicator *applicator,
|
||||
GeglRectangle **rectangles,
|
||||
gint *n_rectangles);
|
||||
|
||||
void pika_applicator_set_crop (PikaApplicator *applicator,
|
||||
const GeglRectangle *rect);
|
||||
const GeglRectangle * pika_applicator_get_crop (PikaApplicator *applicator);
|
||||
|
||||
void pika_applicator_blit (PikaApplicator *applicator,
|
||||
const GeglRectangle *rect);
|
||||
|
||||
|
||||
#endif /* __PIKA_APPLICATOR_H__ */
|
772
app/gegl/pikatilehandlervalidate.c
Normal file
772
app/gegl/pikatilehandlervalidate.c
Normal file
@ -0,0 +1,772 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gegl.h>
|
||||
|
||||
#include "libpikabase/pikabase.h"
|
||||
|
||||
#include "pika-gegl-types.h"
|
||||
|
||||
#include "core/pikachunkiterator.h"
|
||||
|
||||
#include "pika-gegl-loops.h"
|
||||
#include "pika-gegl-utils.h"
|
||||
#include "pikatilehandlervalidate.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
INVALIDATED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FORMAT,
|
||||
PROP_TILE_WIDTH,
|
||||
PROP_TILE_HEIGHT,
|
||||
PROP_WHOLE_TILE
|
||||
};
|
||||
|
||||
|
||||
static void pika_tile_handler_validate_finalize (GObject *object);
|
||||
static void pika_tile_handler_validate_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void pika_tile_handler_validate_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void pika_tile_handler_validate_real_begin_validate (PikaTileHandlerValidate *validate);
|
||||
static void pika_tile_handler_validate_real_end_validate (PikaTileHandlerValidate *validate);
|
||||
static void pika_tile_handler_validate_real_validate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
const Babl *format,
|
||||
gpointer dest_buf,
|
||||
gint dest_stride);
|
||||
static void pika_tile_handler_validate_real_validate_buffer (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
GeglBuffer *buffer);
|
||||
|
||||
static gpointer pika_tile_handler_validate_command (GeglTileSource *source,
|
||||
GeglTileCommand command,
|
||||
gint x,
|
||||
gint y,
|
||||
gint z,
|
||||
gpointer data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (PikaTileHandlerValidate, pika_tile_handler_validate,
|
||||
GEGL_TYPE_TILE_HANDLER)
|
||||
|
||||
#define parent_class pika_tile_handler_validate_parent_class
|
||||
|
||||
static guint pika_tile_handler_validate_signals[LAST_SIGNAL];
|
||||
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_class_init (PikaTileHandlerValidateClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
pika_tile_handler_validate_signals[INVALIDATED] =
|
||||
g_signal_new ("invalidated",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (PikaTileHandlerValidateClass, invalidated),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOXED,
|
||||
G_TYPE_NONE, 1,
|
||||
GEGL_TYPE_RECTANGLE);
|
||||
|
||||
object_class->finalize = pika_tile_handler_validate_finalize;
|
||||
object_class->set_property = pika_tile_handler_validate_set_property;
|
||||
object_class->get_property = pika_tile_handler_validate_get_property;
|
||||
|
||||
klass->begin_validate = pika_tile_handler_validate_real_begin_validate;
|
||||
klass->end_validate = pika_tile_handler_validate_real_end_validate;
|
||||
klass->validate = pika_tile_handler_validate_real_validate;
|
||||
klass->validate_buffer = pika_tile_handler_validate_real_validate_buffer;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_FORMAT,
|
||||
g_param_spec_pointer ("format", NULL, NULL,
|
||||
PIKA_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_TILE_WIDTH,
|
||||
g_param_spec_int ("tile-width", NULL, NULL,
|
||||
1, G_MAXINT, 1,
|
||||
PIKA_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_TILE_HEIGHT,
|
||||
g_param_spec_int ("tile-height", NULL, NULL,
|
||||
1, G_MAXINT, 1,
|
||||
PIKA_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_WHOLE_TILE,
|
||||
g_param_spec_boolean ("whole-tile", NULL, NULL,
|
||||
FALSE,
|
||||
PIKA_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT));
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_init (PikaTileHandlerValidate *validate)
|
||||
{
|
||||
GeglTileSource *source = GEGL_TILE_SOURCE (validate);
|
||||
|
||||
source->command = pika_tile_handler_validate_command;
|
||||
|
||||
validate->dirty_region = cairo_region_create ();
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_finalize (GObject *object)
|
||||
{
|
||||
PikaTileHandlerValidate *validate = PIKA_TILE_HANDLER_VALIDATE (object);
|
||||
|
||||
g_clear_object (&validate->graph);
|
||||
g_clear_pointer (&validate->dirty_region, cairo_region_destroy);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PikaTileHandlerValidate *validate = PIKA_TILE_HANDLER_VALIDATE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FORMAT:
|
||||
validate->format = g_value_get_pointer (value);
|
||||
break;
|
||||
case PROP_TILE_WIDTH:
|
||||
validate->tile_width = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_TILE_HEIGHT:
|
||||
validate->tile_height = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_WHOLE_TILE:
|
||||
validate->whole_tile = g_value_get_boolean (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PikaTileHandlerValidate *validate = PIKA_TILE_HANDLER_VALIDATE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FORMAT:
|
||||
g_value_set_pointer (value, (gpointer) validate->format);
|
||||
break;
|
||||
case PROP_TILE_WIDTH:
|
||||
g_value_set_int (value, validate->tile_width);
|
||||
break;
|
||||
case PROP_TILE_HEIGHT:
|
||||
g_value_set_int (value, validate->tile_height);
|
||||
break;
|
||||
case PROP_WHOLE_TILE:
|
||||
g_value_set_boolean (value, validate->whole_tile);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_real_begin_validate (PikaTileHandlerValidate *validate)
|
||||
{
|
||||
validate->suspend_validate++;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_real_end_validate (PikaTileHandlerValidate *validate)
|
||||
{
|
||||
validate->suspend_validate--;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_real_validate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
const Babl *format,
|
||||
gpointer dest_buf,
|
||||
gint dest_stride)
|
||||
{
|
||||
#if 0
|
||||
g_printerr ("validating at %d %d %d %d\n",
|
||||
rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height);
|
||||
#endif
|
||||
|
||||
gegl_node_blit (validate->graph, 1.0, rect, format,
|
||||
dest_buf, dest_stride,
|
||||
GEGL_BLIT_DEFAULT);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_tile_handler_validate_real_validate_buffer (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
GeglBuffer *buffer)
|
||||
{
|
||||
PikaTileHandlerValidateClass *klass;
|
||||
|
||||
klass = PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate);
|
||||
|
||||
if (klass->validate == pika_tile_handler_validate_real_validate)
|
||||
{
|
||||
gegl_node_blit_buffer (validate->graph, buffer, rect, 0,
|
||||
GEGL_ABYSS_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Babl *format = gegl_buffer_get_format (buffer);
|
||||
gpointer data;
|
||||
gint stride;
|
||||
|
||||
data = gegl_buffer_linear_open (buffer, rect, &stride, format);
|
||||
|
||||
klass->validate (validate, rect, format, data, stride);
|
||||
|
||||
gegl_buffer_linear_close (buffer, data);
|
||||
}
|
||||
}
|
||||
|
||||
static GeglTile *
|
||||
pika_tile_handler_validate_validate_tile (GeglTileSource *source,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
PikaTileHandlerValidate *validate = PIKA_TILE_HANDLER_VALIDATE (source);
|
||||
GeglTile *tile;
|
||||
cairo_rectangle_int_t tile_rect;
|
||||
cairo_region_overlap_t overlap;
|
||||
|
||||
if (validate->suspend_validate ||
|
||||
cairo_region_is_empty (validate->dirty_region))
|
||||
{
|
||||
return gegl_tile_handler_source_command (source,
|
||||
GEGL_TILE_GET, x, y, 0, NULL);
|
||||
}
|
||||
|
||||
tile_rect.x = x * validate->tile_width;
|
||||
tile_rect.y = y * validate->tile_height;
|
||||
tile_rect.width = validate->tile_width;
|
||||
tile_rect.height = validate->tile_height;
|
||||
|
||||
overlap = cairo_region_contains_rectangle (validate->dirty_region,
|
||||
&tile_rect);
|
||||
|
||||
if (overlap == CAIRO_REGION_OVERLAP_OUT)
|
||||
{
|
||||
return gegl_tile_handler_source_command (source,
|
||||
GEGL_TILE_GET, x, y, 0, NULL);
|
||||
}
|
||||
|
||||
if (overlap == CAIRO_REGION_OVERLAP_IN || validate->whole_tile)
|
||||
{
|
||||
gint tile_bpp;
|
||||
gint tile_stride;
|
||||
|
||||
cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect);
|
||||
|
||||
tile_bpp = babl_format_get_bytes_per_pixel (validate->format);
|
||||
tile_stride = tile_bpp * validate->tile_width;
|
||||
|
||||
tile = gegl_tile_handler_get_source_tile (GEGL_TILE_HANDLER (source),
|
||||
x, y, 0, FALSE);
|
||||
|
||||
pika_tile_handler_validate_begin_validate (validate);
|
||||
|
||||
gegl_tile_lock (tile);
|
||||
|
||||
PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate
|
||||
(validate,
|
||||
GEGL_RECTANGLE (tile_rect.x,
|
||||
tile_rect.y,
|
||||
tile_rect.width,
|
||||
tile_rect.height),
|
||||
validate->format,
|
||||
gegl_tile_get_data (tile),
|
||||
tile_stride);
|
||||
|
||||
gegl_tile_unlock (tile);
|
||||
|
||||
pika_tile_handler_validate_end_validate (validate);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_region_t *tile_region;
|
||||
gint tile_bpp;
|
||||
gint tile_stride;
|
||||
gint n_rects;
|
||||
gint i;
|
||||
|
||||
tile_region = cairo_region_copy (validate->dirty_region);
|
||||
cairo_region_intersect_rectangle (tile_region, &tile_rect);
|
||||
|
||||
cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect);
|
||||
|
||||
tile_bpp = babl_format_get_bytes_per_pixel (validate->format);
|
||||
tile_stride = tile_bpp * validate->tile_width;
|
||||
|
||||
tile = gegl_tile_handler_source_command (source,
|
||||
GEGL_TILE_GET, x, y, 0, NULL);
|
||||
|
||||
if (! tile)
|
||||
{
|
||||
tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source),
|
||||
x, y, 0);
|
||||
|
||||
memset (gegl_tile_get_data (tile),
|
||||
0, tile_stride * validate->tile_height);
|
||||
}
|
||||
|
||||
pika_tile_handler_validate_begin_validate (validate);
|
||||
|
||||
gegl_tile_lock (tile);
|
||||
|
||||
n_rects = cairo_region_num_rectangles (tile_region);
|
||||
|
||||
#if 0
|
||||
g_printerr ("%d chunks\n", n_rects);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
cairo_rectangle_int_t blit_rect;
|
||||
gint tile_x;
|
||||
gint tile_y;
|
||||
|
||||
cairo_region_get_rectangle (tile_region, i, &blit_rect);
|
||||
|
||||
tile_x = blit_rect.x % validate->tile_width;
|
||||
if (tile_x < 0) tile_x += validate->tile_width;
|
||||
|
||||
tile_y = blit_rect.y % validate->tile_height;
|
||||
if (tile_y < 0) tile_y += validate->tile_height;
|
||||
|
||||
PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate
|
||||
(validate,
|
||||
GEGL_RECTANGLE (blit_rect.x,
|
||||
blit_rect.y,
|
||||
blit_rect.width,
|
||||
blit_rect.height),
|
||||
validate->format,
|
||||
gegl_tile_get_data (tile) +
|
||||
tile_y * tile_stride +
|
||||
tile_x * tile_bpp,
|
||||
tile_stride);
|
||||
}
|
||||
|
||||
gegl_tile_unlock (tile);
|
||||
|
||||
pika_tile_handler_validate_end_validate (validate);
|
||||
|
||||
cairo_region_destroy (tile_region);
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
pika_tile_handler_validate_command (GeglTileSource *source,
|
||||
GeglTileCommand command,
|
||||
gint x,
|
||||
gint y,
|
||||
gint z,
|
||||
gpointer data)
|
||||
{
|
||||
if (command == GEGL_TILE_GET && z == 0)
|
||||
return pika_tile_handler_validate_validate_tile (source, x, y);
|
||||
|
||||
return gegl_tile_handler_source_command (source, command, x, y, z, data);
|
||||
}
|
||||
|
||||
|
||||
/* public functions */
|
||||
|
||||
GeglTileHandler *
|
||||
pika_tile_handler_validate_new (GeglNode *graph)
|
||||
{
|
||||
PikaTileHandlerValidate *validate;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_NODE (graph), NULL);
|
||||
|
||||
validate = g_object_new (PIKA_TYPE_TILE_HANDLER_VALIDATE, NULL);
|
||||
|
||||
validate->graph = g_object_ref (graph);
|
||||
|
||||
return GEGL_TILE_HANDLER (validate);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_assign (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (buffer));
|
||||
g_return_if_fail (pika_tile_handler_validate_get_assigned (buffer) == NULL);
|
||||
|
||||
gegl_buffer_add_handler (buffer, validate);
|
||||
|
||||
g_object_get (buffer,
|
||||
"format", &validate->format,
|
||||
"tile-width", &validate->tile_width,
|
||||
"tile-height", &validate->tile_height,
|
||||
NULL);
|
||||
|
||||
g_object_set_data (G_OBJECT (buffer),
|
||||
"pika-tile-handler-validate", validate);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_unassign (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (buffer));
|
||||
g_return_if_fail (pika_tile_handler_validate_get_assigned (buffer) == validate);
|
||||
|
||||
g_object_set_data (G_OBJECT (buffer),
|
||||
"pika-tile-handler-validate", NULL);
|
||||
|
||||
gegl_buffer_remove_handler (buffer, validate);
|
||||
}
|
||||
|
||||
PikaTileHandlerValidate *
|
||||
pika_tile_handler_validate_get_assigned (GeglBuffer *buffer)
|
||||
{
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
|
||||
|
||||
return g_object_get_data (G_OBJECT (buffer),
|
||||
"pika-tile-handler-validate");
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_invalidate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (rect != NULL);
|
||||
|
||||
cairo_region_union_rectangle (validate->dirty_region,
|
||||
(cairo_rectangle_int_t *) rect);
|
||||
|
||||
gegl_tile_handler_damage_rect (GEGL_TILE_HANDLER (validate), rect);
|
||||
|
||||
g_signal_emit (validate, pika_tile_handler_validate_signals[INVALIDATED],
|
||||
0, rect, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_undo_invalidate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (rect != NULL);
|
||||
|
||||
cairo_region_subtract_rectangle (validate->dirty_region,
|
||||
(cairo_rectangle_int_t *) rect);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_begin_validate (PikaTileHandlerValidate *validate)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
|
||||
if (validate->validating++ == 0)
|
||||
PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->begin_validate (validate);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_end_validate (PikaTileHandlerValidate *validate)
|
||||
{
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (validate->validating > 0);
|
||||
|
||||
if (--validate->validating == 0)
|
||||
PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->end_validate (validate);
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_validate (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer,
|
||||
const GeglRectangle *rect,
|
||||
gboolean intersect,
|
||||
gboolean chunked)
|
||||
{
|
||||
PikaTileHandlerValidateClass *klass;
|
||||
cairo_region_t *region = NULL;
|
||||
|
||||
g_return_if_fail (PIKA_IS_TILE_HANDLER_VALIDATE (validate));
|
||||
g_return_if_fail (pika_tile_handler_validate_get_assigned (buffer) ==
|
||||
validate);
|
||||
|
||||
klass = PIKA_TILE_HANDLER_VALIDATE_GET_CLASS (validate);
|
||||
|
||||
if (! rect)
|
||||
rect = gegl_buffer_get_extent (buffer);
|
||||
|
||||
if (intersect)
|
||||
{
|
||||
region = cairo_region_copy (validate->dirty_region);
|
||||
|
||||
cairo_region_intersect_rectangle (region,
|
||||
(const cairo_rectangle_int_t *) rect);
|
||||
}
|
||||
else if (chunked)
|
||||
{
|
||||
region = cairo_region_create_rectangle (
|
||||
(const cairo_rectangle_int_t *) rect);
|
||||
}
|
||||
|
||||
if (region)
|
||||
{
|
||||
if (! cairo_region_is_empty (region))
|
||||
{
|
||||
pika_tile_handler_validate_begin_validate (validate);
|
||||
|
||||
if (chunked)
|
||||
{
|
||||
PikaChunkIterator *iter;
|
||||
|
||||
iter = pika_chunk_iterator_new (region);
|
||||
region = NULL;
|
||||
|
||||
while (pika_chunk_iterator_next (iter))
|
||||
{
|
||||
GeglRectangle blit_rect;
|
||||
|
||||
while (pika_chunk_iterator_get_rect (iter, &blit_rect))
|
||||
klass->validate_buffer (validate, &blit_rect, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gint n_rects;
|
||||
gint i;
|
||||
|
||||
n_rects = cairo_region_num_rectangles (region);
|
||||
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
cairo_rectangle_int_t blit_rect;
|
||||
|
||||
cairo_region_get_rectangle (region, i, &blit_rect);
|
||||
|
||||
klass->validate_buffer (validate,
|
||||
(const GeglRectangle *) &blit_rect,
|
||||
buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pika_tile_handler_validate_end_validate (validate);
|
||||
|
||||
cairo_region_subtract_rectangle (
|
||||
validate->dirty_region,
|
||||
(const cairo_rectangle_int_t *) rect);
|
||||
}
|
||||
|
||||
g_clear_pointer (®ion, cairo_region_destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_tile_handler_validate_begin_validate (validate);
|
||||
|
||||
klass->validate_buffer (validate, rect, buffer);
|
||||
|
||||
pika_tile_handler_validate_end_validate (validate);
|
||||
|
||||
cairo_region_subtract_rectangle (
|
||||
validate->dirty_region,
|
||||
(const cairo_rectangle_int_t *) rect);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
pika_tile_handler_validate_buffer_set_extent (GeglBuffer *buffer,
|
||||
const GeglRectangle *extent)
|
||||
{
|
||||
PikaTileHandlerValidate *validate;
|
||||
|
||||
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
|
||||
g_return_val_if_fail (extent != NULL, FALSE);
|
||||
|
||||
validate = pika_tile_handler_validate_get_assigned (buffer);
|
||||
|
||||
g_return_val_if_fail (validate != NULL, FALSE);
|
||||
|
||||
validate->suspend_validate++;
|
||||
|
||||
if (pika_gegl_buffer_set_extent (buffer, extent))
|
||||
{
|
||||
validate->suspend_validate--;
|
||||
|
||||
cairo_region_intersect_rectangle (validate->dirty_region,
|
||||
(const cairo_rectangle_int_t *) extent);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
validate->suspend_validate--;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
pika_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
GeglBuffer *dst_buffer,
|
||||
const GeglRectangle *dst_rect)
|
||||
{
|
||||
PikaTileHandlerValidate *src_validate;
|
||||
PikaTileHandlerValidate *dst_validate;
|
||||
GeglRectangle real_src_rect;
|
||||
GeglRectangle real_dst_rect;
|
||||
|
||||
g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
|
||||
g_return_if_fail (GEGL_IS_BUFFER (dst_buffer));
|
||||
g_return_if_fail (src_rect != dst_rect);
|
||||
|
||||
src_validate = pika_tile_handler_validate_get_assigned (src_buffer);
|
||||
dst_validate = pika_tile_handler_validate_get_assigned (dst_buffer);
|
||||
|
||||
g_return_if_fail (dst_validate != NULL);
|
||||
|
||||
if (! src_rect)
|
||||
src_rect = gegl_buffer_get_extent (src_buffer);
|
||||
|
||||
if (! dst_rect)
|
||||
dst_rect = src_rect;
|
||||
|
||||
real_src_rect = *src_rect;
|
||||
|
||||
gegl_rectangle_intersect (&real_dst_rect,
|
||||
dst_rect, gegl_buffer_get_extent (dst_buffer));
|
||||
|
||||
real_src_rect.x += real_dst_rect.x - dst_rect->x;
|
||||
real_src_rect.y += real_dst_rect.y - dst_rect->y;
|
||||
real_src_rect.width -= real_dst_rect.x - dst_rect->x;
|
||||
real_src_rect.height -= real_dst_rect.y - dst_rect->y;
|
||||
|
||||
real_src_rect.width = CLAMP (real_src_rect.width, 0, real_dst_rect.width);
|
||||
real_src_rect.height = CLAMP (real_src_rect.height, 0, real_dst_rect.height);
|
||||
|
||||
/* temporarily remove the source buffer's validate handler, so that
|
||||
* gegl_buffer_copy() can use fast tile copying, using the TILE_COPY command.
|
||||
* currently, gegl only uses TILE_COPY when the source buffer has no user-
|
||||
* provided tile handlers.
|
||||
*/
|
||||
if (src_validate)
|
||||
{
|
||||
g_object_ref (src_validate);
|
||||
|
||||
pika_tile_handler_validate_unassign (src_validate, src_buffer);
|
||||
}
|
||||
|
||||
dst_validate->suspend_validate++;
|
||||
|
||||
pika_gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE,
|
||||
dst_buffer, &real_dst_rect);
|
||||
|
||||
dst_validate->suspend_validate--;
|
||||
|
||||
if (src_validate)
|
||||
{
|
||||
pika_tile_handler_validate_assign (src_validate, src_buffer);
|
||||
|
||||
g_object_unref (src_validate);
|
||||
}
|
||||
|
||||
cairo_region_subtract_rectangle (dst_validate->dirty_region,
|
||||
(cairo_rectangle_int_t *) &real_dst_rect);
|
||||
|
||||
if (src_validate)
|
||||
{
|
||||
if (real_src_rect.x == real_dst_rect.x &&
|
||||
real_src_rect.y == real_dst_rect.y &&
|
||||
gegl_rectangle_equal (&real_src_rect,
|
||||
gegl_buffer_get_extent (src_buffer)))
|
||||
{
|
||||
cairo_region_union (dst_validate->dirty_region,
|
||||
src_validate->dirty_region);
|
||||
}
|
||||
else if (cairo_region_contains_rectangle (
|
||||
src_validate->dirty_region,
|
||||
(cairo_rectangle_int_t *) &real_src_rect) !=
|
||||
CAIRO_REGION_OVERLAP_OUT)
|
||||
{
|
||||
cairo_region_t *region;
|
||||
|
||||
region = cairo_region_copy (src_validate->dirty_region);
|
||||
|
||||
if (! gegl_rectangle_equal (&real_src_rect,
|
||||
gegl_buffer_get_extent (src_buffer)))
|
||||
{
|
||||
cairo_region_intersect_rectangle (
|
||||
region, (cairo_rectangle_int_t *) &real_src_rect);
|
||||
}
|
||||
|
||||
cairo_region_translate (region,
|
||||
real_dst_rect.x - real_src_rect.x,
|
||||
real_dst_rect.y - real_src_rect.y);
|
||||
|
||||
if (cairo_region_is_empty (dst_validate->dirty_region))
|
||||
{
|
||||
cairo_region_destroy (dst_validate->dirty_region);
|
||||
|
||||
dst_validate->dirty_region = region;
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_region_union (dst_validate->dirty_region, region);
|
||||
|
||||
cairo_region_destroy (region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
116
app/gegl/pikatilehandlervalidate.h
Normal file
116
app/gegl/pikatilehandlervalidate.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __PIKA_TILE_HANDLER_VALIDATE_H__
|
||||
#define __PIKA_TILE_HANDLER_VALIDATE_H__
|
||||
|
||||
#include <gegl-buffer-backend.h>
|
||||
|
||||
/***
|
||||
* PikaTileHandlerValidate is a GeglTileHandler that renders the
|
||||
* projection.
|
||||
*/
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PIKA_TYPE_TILE_HANDLER_VALIDATE (pika_tile_handler_validate_get_type ())
|
||||
#define PIKA_TILE_HANDLER_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_TILE_HANDLER_VALIDATE, PikaTileHandlerValidate))
|
||||
#define PIKA_TILE_HANDLER_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_TILE_HANDLER_VALIDATE, PikaTileHandlerValidateClass))
|
||||
#define PIKA_IS_TILE_HANDLER_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_TILE_HANDLER_VALIDATE))
|
||||
#define PIKA_IS_TILE_HANDLER_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_TILE_HANDLER_VALIDATE))
|
||||
#define PIKA_TILE_HANDLER_VALIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_TILE_HANDLER_VALIDATE, PikaTileHandlerValidateClass))
|
||||
|
||||
|
||||
typedef struct _PikaTileHandlerValidate PikaTileHandlerValidate;
|
||||
typedef struct _PikaTileHandlerValidateClass PikaTileHandlerValidateClass;
|
||||
|
||||
struct _PikaTileHandlerValidate
|
||||
{
|
||||
GeglTileHandler parent_instance;
|
||||
|
||||
GeglNode *graph;
|
||||
cairo_region_t *dirty_region;
|
||||
const Babl *format;
|
||||
gint tile_width;
|
||||
gint tile_height;
|
||||
gboolean whole_tile;
|
||||
gint validating;
|
||||
gint suspend_validate;
|
||||
};
|
||||
|
||||
struct _PikaTileHandlerValidateClass
|
||||
{
|
||||
GeglTileHandlerClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (* invalidated) (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect);
|
||||
|
||||
/* virtual functions */
|
||||
void (* begin_validate) (PikaTileHandlerValidate *validate);
|
||||
void (* end_validate) (PikaTileHandlerValidate *validate);
|
||||
void (* validate) (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
const Babl *format,
|
||||
gpointer dest_buf,
|
||||
gint dest_stride);
|
||||
void (* validate_buffer) (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect,
|
||||
GeglBuffer *buffer);
|
||||
};
|
||||
|
||||
|
||||
GType pika_tile_handler_validate_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GeglTileHandler * pika_tile_handler_validate_new (GeglNode *graph);
|
||||
|
||||
void pika_tile_handler_validate_assign (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer);
|
||||
void pika_tile_handler_validate_unassign (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer);
|
||||
PikaTileHandlerValidate * pika_tile_handler_validate_get_assigned (GeglBuffer *buffer);
|
||||
|
||||
void pika_tile_handler_validate_invalidate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect);
|
||||
void pika_tile_handler_validate_undo_invalidate (PikaTileHandlerValidate *validate,
|
||||
const GeglRectangle *rect);
|
||||
|
||||
void pika_tile_handler_validate_begin_validate (PikaTileHandlerValidate *validate);
|
||||
void pika_tile_handler_validate_end_validate (PikaTileHandlerValidate *validate);
|
||||
|
||||
void pika_tile_handler_validate_validate (PikaTileHandlerValidate *validate,
|
||||
GeglBuffer *buffer,
|
||||
const GeglRectangle *rect,
|
||||
gboolean intersect,
|
||||
gboolean chunked);
|
||||
|
||||
gboolean pika_tile_handler_validate_buffer_set_extent (GeglBuffer *buffer,
|
||||
const GeglRectangle *extent);
|
||||
|
||||
void pika_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
GeglBuffer *dst_buffer,
|
||||
const GeglRectangle *dst_rect);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PIKA_TILE_HANDLER_VALIDATE_H__ */
|
Reference in New Issue
Block a user