Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

60
app/gegl/meson.build Normal file
View 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
View 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);
}

View 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

File diff suppressed because it is too large Load Diff

69
app/gegl/pika-babl.h Normal file
View 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__ */

View 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);
}

View 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__ */

View 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 */

View 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__ */

View 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 */

View 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

File diff suppressed because it is too large Load Diff

113
app/gegl/pika-gegl-loops.h Normal file
View 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__ */

View 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" */

View 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
View 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
View 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
View 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);
}

View 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__ */

View 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;
}

View 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__ */

View 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
View 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);
}

View 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
View 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
View 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
View 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
View 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__ */

View 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 (&region, 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);
}
}
}
}

View 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__ */