diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /app/gegl/gimp-gegl-loops.cc | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/gegl/gimp-gegl-loops.cc')
-rw-r--r-- | app/gegl/gimp-gegl-loops.cc | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/app/gegl/gimp-gegl-loops.cc b/app/gegl/gimp-gegl-loops.cc new file mode 100644 index 0000000..4564bcd --- /dev/null +++ b/app/gegl/gimp-gegl-loops.cc @@ -0,0 +1,1089 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-loops.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 <gegl-buffer-backend.h> + +extern "C" +{ + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "gimp-gegl-types.h" + +#include "gimp-babl.h" +#include "gimp-gegl-loops.h" +#include "gimp-gegl-loops-sse2.h" + +#include "core/gimp-atomic.h" +#include "core/gimp-utils.h" +#include "core/gimpprogress.h" + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + +#define SHIFTED_AREA(dest, src) \ + const GeglRectangle dest##_area_ = { \ + src##_area->x + (dest##_rect->x - src##_rect->x), \ + src##_area->y + (dest##_rect->y - src##_rect->y), \ + src##_area->width, src##_area->height \ + }; \ + const GeglRectangle * const dest##_area = &dest##_area_ + + +void +gimp_gegl_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglAbyssPolicy abyss_policy, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect) +{ + GeglRectangle real_dest_rect; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = src_rect; + + real_dest_rect = *dest_rect; + real_dest_rect.width = src_rect->width; + real_dest_rect.height = src_rect->height; + + dest_rect = &real_dest_rect; + + if (gegl_buffer_get_format (src_buffer) == + gegl_buffer_get_format (dest_buffer)) + { + gboolean skip_abyss = FALSE; + GeglRectangle src_abyss; + GeglRectangle dest_abyss; + + if (abyss_policy == GEGL_ABYSS_NONE) + { + src_abyss = *gegl_buffer_get_abyss (src_buffer); + dest_abyss = *gegl_buffer_get_abyss (dest_buffer); + + skip_abyss = ! (gegl_rectangle_contains (&src_abyss, src_rect) && + gegl_rectangle_contains (&dest_abyss, dest_rect)); + } + + if (skip_abyss) + { + if (src_buffer < dest_buffer) + { + gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer)); + gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer)); + } + else + { + gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer)); + gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer)); + } + + gegl_buffer_set_abyss (src_buffer, src_rect); + gegl_buffer_set_abyss (dest_buffer, dest_rect); + } + + gegl_buffer_copy (src_buffer, src_rect, abyss_policy, + dest_buffer, dest_rect); + + if (skip_abyss) + { + gegl_buffer_set_abyss (src_buffer, &src_abyss); + gegl_buffer_set_abyss (dest_buffer, &dest_abyss); + + gegl_tile_handler_unlock (GEGL_TILE_HANDLER (src_buffer)); + gegl_tile_handler_unlock (GEGL_TILE_HANDLER (dest_buffer)); + } + } + else + { + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + SHIFTED_AREA (dest, src); + + gegl_buffer_copy (src_buffer, src_area, abyss_policy, + dest_buffer, dest_area); + }); + } +} + +void +gimp_gegl_clear (GeglBuffer *buffer, + const GeglRectangle *rect) +{ + const Babl *format; + gint bpp; + gint n_components; + gint bpc; + gint alpha_offset; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + format = gegl_buffer_get_format (buffer); + + if (! babl_format_has_alpha (format)) + return; + + bpp = babl_format_get_bytes_per_pixel (format); + n_components = babl_format_get_n_components (format); + bpc = bpp / n_components; + alpha_offset = (n_components - 1) * bpc; + + gegl_parallel_distribute_area ( + rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + GeglBufferIterator *iter; + + iter = gegl_buffer_iterator_new (buffer, area, 0, format, + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, + 1); + + while (gegl_buffer_iterator_next (iter)) + { + guint8 *data = (guint8 *) iter->items[0].data; + gint i; + + data += alpha_offset; + + for (i = 0; i < iter->length; i++) + { + memset (data, 0, bpc); + + data += bpp; + } + } + }); +} + +void +gimp_gegl_convolve (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + const gfloat *kernel, + gint kernel_size, + gdouble divisor, + GimpConvolutionType mode, + gboolean alpha_weighting) +{ + gfloat *src; + gint src_rowstride; + + const Babl *src_format; + const Babl *dest_format; + gint src_components; + gint dest_components; + gfloat offset; + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + src_format = gegl_buffer_get_format (src_buffer); + + if (babl_format_is_palette (src_format)) + src_format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (src_format)); + else + src_format = gimp_babl_format (gimp_babl_format_get_base_type (src_format), + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (src_format)); + + dest_format = gegl_buffer_get_format (dest_buffer); + + if (babl_format_is_palette (dest_format)) + dest_format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (dest_format)); + else + dest_format = gimp_babl_format (gimp_babl_format_get_base_type (dest_format), + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (dest_format)); + + src_components = babl_format_get_n_components (src_format); + dest_components = babl_format_get_n_components (dest_format); + + /* Get source pixel data */ + src_rowstride = src_components * src_rect->width; + src = g_new (gfloat, src_rowstride * src_rect->height); + gegl_buffer_get (src_buffer, src_rect, 1.0, src_format, src, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + /* If the mode is NEGATIVE_CONVOL, the offset should be 0.5 */ + if (mode == GIMP_NEGATIVE_CONVOL) + { + offset = 0.5; + mode = GIMP_NORMAL_CONVOL; + } + else + { + offset = 0.0; + } + + gegl_parallel_distribute_area ( + dest_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *dest_area) + { + const gint components = src_components; + const gint a_component = components - 1; + const gint margin = kernel_size / 2; + GeglBufferIterator *dest_iter; + + /* Set up dest iterator */ + dest_iter = gegl_buffer_iterator_new (dest_buffer, dest_area, 0, dest_format, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (dest_iter)) + { + /* Convolve the src image using the convolution kernel, writing + * to dest Convolve is not tile-enabled--use accordingly + */ + gfloat *dest = (gfloat *) dest_iter->items[0].data; + const gint x1 = 0; + const gint y1 = 0; + const gint x2 = src_rect->width - 1; + const gint y2 = src_rect->height - 1; + const gint dest_x1 = dest_iter->items[0].roi.x; + const gint dest_y1 = dest_iter->items[0].roi.y; + const gint dest_x2 = dest_iter->items[0].roi.x + dest_iter->items[0].roi.width; + const gint dest_y2 = dest_iter->items[0].roi.y + dest_iter->items[0].roi.height; + gint x, y; + + for (y = dest_y1; y < dest_y2; y++) + { + gfloat *d = dest; + + if (alpha_weighting) + { + for (x = dest_x1; x < dest_x2; x++) + { + const gfloat *m = kernel; + gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; + gdouble weighted_divisor = 0.0; + gint i, j, b; + + for (j = y - margin; j <= y + margin; j++) + { + for (i = x - margin; i <= x + margin; i++, m++) + { + gint xx = CLAMP (i, x1, x2); + gint yy = CLAMP (j, y1, y2); + const gfloat *s = src + yy * src_rowstride + xx * components; + const gfloat a = s[a_component]; + + if (a) + { + gdouble mult_alpha = *m * a; + + weighted_divisor += mult_alpha; + + for (b = 0; b < a_component; b++) + total[b] += mult_alpha * s[b]; + + total[a_component] += mult_alpha; + } + } + } + + if (weighted_divisor == 0.0) + weighted_divisor = divisor; + + for (b = 0; b < a_component; b++) + total[b] /= weighted_divisor; + + total[a_component] /= divisor; + + for (b = 0; b < components; b++) + { + total[b] += offset; + + if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) + total[b] = - total[b]; + + *d++ = CLAMP (total[b], 0.0, 1.0); + } + } + } + else + { + for (x = dest_x1; x < dest_x2; x++) + { + const gfloat *m = kernel; + gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; + gint i, j, b; + + for (j = y - margin; j <= y + margin; j++) + { + for (i = x - margin; i <= x + margin; i++, m++) + { + gint xx = CLAMP (i, x1, x2); + gint yy = CLAMP (j, y1, y2); + const gfloat *s = src + yy * src_rowstride + xx * components; + + for (b = 0; b < components; b++) + total[b] += *m * s[b]; + } + } + + for (b = 0; b < components; b++) + { + total[b] = total[b] / divisor + offset; + + if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) + total[b] = - total[b]; + + *d++ = CLAMP (total[b], 0.0, 1.0); + } + } + } + + dest += dest_iter->items[0].roi.width * dest_components; + } + } + }); + + g_free (src); +} + +static inline gfloat +odd_powf (gfloat x, + gfloat y) +{ + if (x >= 0.0f) + return powf ( x, y); + else + return -powf (-x, y); +} + +void +gimp_gegl_dodgeburn (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble exposure, + GimpDodgeBurnType type, + GimpTransferMode mode) +{ + if (type == GIMP_DODGE_BURN_TYPE_BURN) + exposure = -exposure; + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, src); + + iter = gegl_buffer_iterator_new (src_buffer, src_area, 0, + babl_format ("R'G'B'A float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("R'G'B'A float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + switch (mode) + { + gfloat factor; + + case GIMP_TRANSFER_HIGHLIGHTS: + factor = 1.0 + exposure * (0.333333); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest++ = *src++ * factor; + *dest++ = *src++ * factor; + *dest++ = *src++ * factor; + + *dest++ = *src++; + } + } + break; + + case GIMP_TRANSFER_MIDTONES: + if (exposure < 0) + factor = 1.0 - exposure * (0.333333); + else + factor = 1.0 / (1.0 + exposure); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest++ = odd_powf (*src++, factor); + *dest++ = odd_powf (*src++, factor); + *dest++ = odd_powf (*src++, factor); + + *dest++ = *src++; + } + } + break; + + case GIMP_TRANSFER_SHADOWS: + if (exposure >= 0) + factor = 0.333333 * exposure; + else + factor = -0.333333 * exposure; + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + if (exposure >= 0) + { + gfloat s; + + s = *src++; *dest++ = factor + s - factor * s; + s = *src++; *dest++ = factor + s - factor * s; + s = *src++; *dest++ = factor + s - factor * s; + } + else + { + gfloat s; + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + } + + *dest++ = *src++; + } + } + break; + } + }); +} + +/* helper function of gimp_gegl_smudge_with_paint_process() + src and dest can be the same address + */ +static inline void +gimp_gegl_smudge_with_paint_blend (const gfloat *src1, + gfloat src1_rate, + const gfloat *src2, + gfloat src2_rate, + gfloat *dest, + gboolean no_erasing_src2) +{ + gfloat orginal_src2_alpha; + gfloat src1_alpha; + gfloat src2_alpha; + gfloat result_alpha; + gint b; + + orginal_src2_alpha = src2[3]; + src1_alpha = src1_rate * src1[3]; + src2_alpha = src2_rate * orginal_src2_alpha; + result_alpha = src1_alpha + src2_alpha; + + if (result_alpha == 0) + { + memset (dest, 0, sizeof (gfloat) * 4); + return; + } + + for (b = 0; b < 3; b++) + dest[b] = (src1[b] * src1_alpha + src2[b] * src2_alpha) / result_alpha; + + if (no_erasing_src2) + { + result_alpha = MAX (result_alpha, orginal_src2_alpha); + } + + dest[3] = result_alpha; +} + +/* helper function of gimp_gegl_smudge_with_paint() */ +static void +gimp_gegl_smudge_with_paint_process (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 */ + gimp_gegl_smudge_with_paint_blend (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; + + gimp_gegl_smudge_with_paint_blend (src1, flow, accum, 1 - flow, + paint, no_erasing); + } + + accum += 4; + canvas += 4; + paint += 4; + } +} + +/* smudge painting calculation. Currently only smudge tool uses this function + * Accum = rate*Accum + (1-rate)*Canvas + * if brush_color!=NULL + * Paint = flow*brushColor + (1-flow)*Accum + * else + * Paint = flow*Paint + (1-flow)*Accum + */ +void +gimp_gegl_smudge_with_paint (GeglBuffer *accum_buffer, + const GeglRectangle *accum_rect, + GeglBuffer *canvas_buffer, + const GeglRectangle *canvas_rect, + const GimpRGB *brush_color, + GeglBuffer *paint_buffer, + gboolean no_erasing, + gdouble flow, + gdouble rate) +{ + gfloat brush_color_float[4]; + gfloat brush_a = flow; + GeglAccessMode paint_buffer_access_mode = (brush_color ? + GEGL_ACCESS_WRITE : + GEGL_ACCESS_READWRITE); +#if COMPILE_SSE2_INTRINISICS + gboolean sse2 = (gimp_cpu_accel_get_support () & + GIMP_CPU_ACCEL_X86_SSE2); +#endif + + if (! accum_rect) + accum_rect = gegl_buffer_get_extent (accum_buffer); + + if (! canvas_rect) + canvas_rect = gegl_buffer_get_extent (canvas_buffer); + + /* convert brush color from double to float */ + if (brush_color) + { + const gdouble *brush_color_ptr = &brush_color->r; + gint b; + + for (b = 0; b < 4; b++) + brush_color_float[b] = brush_color_ptr[b]; + + brush_a *= brush_color_ptr[3]; + } + + gegl_parallel_distribute_area ( + accum_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *accum_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (canvas, accum); + + iter = gegl_buffer_iterator_new (accum_buffer, accum_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 3); + + gegl_buffer_iterator_add (iter, canvas_buffer, canvas_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + + gegl_buffer_iterator_add (iter, paint_buffer, + GEGL_RECTANGLE (accum_area->x - accum_rect->x, + accum_area->y - accum_rect->y, + 0, 0), + 0, + babl_format ("RGBA float"), + paint_buffer_access_mode, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *accum = (gfloat *) iter->items[0].data; + const gfloat *canvas = (const gfloat *) iter->items[1].data; + gfloat *paint = (gfloat *) iter->items[2].data; + gint count = iter->length; + +#if COMPILE_SSE2_INTRINISICS + if (sse2 && ((guintptr) accum | + (guintptr) canvas | + (guintptr) (brush_color ? brush_color_float : paint) | + (guintptr) paint) % 16 == 0) + { + gimp_gegl_smudge_with_paint_process_sse2 (accum, canvas, paint, count, + brush_color ? brush_color_float : + NULL, + brush_a, + no_erasing, flow, rate); + } + else +#endif + { + gimp_gegl_smudge_with_paint_process (accum, canvas, paint, count, + brush_color ? brush_color_float : + NULL, + brush_a, + no_erasing, flow, rate); + } + } + }); +} + +void +gimp_gegl_apply_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + dest[3] *= *mask * opacity; + + mask += 1; + dest += 4; + } + } + }); +} + +void +gimp_gegl_combine_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest *= *mask * opacity; + + mask += 1; + dest += 1; + } + } + }); +} + +void +gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity, + gboolean stipple) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + if (stipple) + { + while (count--) + { + dest[0] += (1.0 - dest[0]) * *mask * opacity; + + mask += 1; + dest += 1; + } + } + else + { + while (count--) + { + if (opacity > dest[0]) + dest[0] += (opacity - dest[0]) * *mask * opacity; + + mask += 1; + dest += 1; + } + } + } + }); +} + +void +gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, + const GeglRectangle *indexed_rect, + const Babl *indexed_format, + GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + gint index) +{ + if (! indexed_rect) + indexed_rect = gegl_buffer_get_extent (indexed_buffer); + + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + gegl_parallel_distribute_area ( + indexed_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *indexed_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (mask, indexed); + + iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0, + indexed_format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *indexed = (const guchar *) iter->items[0].data; + gfloat *mask = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + if (*indexed == index) + *mask = 1.0; + else + *mask = 0.0; + + indexed++; + mask++; + } + } + }); +} + +static void +gimp_gegl_convert_color_profile_progress (GimpProgress *progress, + gdouble value) +{ + if (gegl_is_main_thread ()) + gimp_progress_set_value (progress, value); +} + +void +gimp_gegl_convert_color_profile (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GimpColorProfile *src_profile, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpColorTransform *transform; + guint flags = 0; + const Babl *src_format; + const Babl *dest_format; + + src_format = gegl_buffer_get_format (src_buffer); + dest_format = gegl_buffer_get_format (dest_buffer); + + if (bpc) + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; + + transform = gimp_color_transform_new (src_profile, src_format, + dest_profile, dest_format, + intent, + (GimpColorTransformFlags) flags); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + if (transform) + { + if (progress) + { + g_signal_connect_swapped ( + transform, "progress", + G_CALLBACK (gimp_gegl_convert_color_profile_progress), + progress); + } + + GIMP_TIMER_START (); + + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + SHIFTED_AREA (dest, src); + + gimp_color_transform_process_buffer (transform, + src_buffer, src_area, + dest_buffer, dest_area); + }); + + GIMP_TIMER_END ("converting buffer"); + + g_object_unref (transform); + } + else + { + gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE, + dest_buffer, dest_rect); + + if (progress) + gimp_progress_set_value (progress, 1.0); + } +} + +void +gimp_gegl_average_color (GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean clip_to_buffer, + GeglAbyssPolicy abyss_policy, + const Babl *format, + gpointer color) +{ + typedef struct + { + gfloat color[4]; + gint n; + } Sum; + + const Babl *average_format = babl_format ("RaGaBaA float"); + GeglRectangle roi; + GSList * volatile sums = NULL; + GSList *list; + Sum average = {}; + gint c; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (color != NULL); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + if (! format) + format = gegl_buffer_get_format (buffer); + + if (clip_to_buffer) + gegl_rectangle_intersect (&roi, rect, gegl_buffer_get_extent (buffer)); + else + roi = *rect; + + gegl_parallel_distribute_area ( + &roi, PIXELS_PER_THREAD, + [&] (const GeglRectangle *area) + { + Sum *sum; + GeglBufferIterator *iter; + gfloat color[4] = {}; + gint n = 0; + + iter = gegl_buffer_iterator_new (buffer, area, 0, average_format, + GEGL_BUFFER_READ, abyss_policy, 1); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *p = (const gfloat *) iter->items[0].data; + gint i; + + for (i = 0; i < iter->length; i++) + { + gint c; + + for (c = 0; c < 4; c++) + color[c] += p[c]; + + p += 4; + } + + n += iter->length; + } + + sum = g_slice_new (Sum); + + memcpy (sum->color, color, sizeof (color)); + sum->n = n; + + gimp_atomic_slist_push_head (&sums, sum); + }); + + for (list = sums; list; list = g_slist_next (list)) + { + Sum *sum = (Sum *) list->data; + + for (c = 0; c < 4; c++) + average.color[c] += sum->color[c]; + + average.n += sum->n; + + g_slice_free (Sum, sum); + } + + g_slist_free (sums); + + if (average.n > 0) + { + for (c = 0; c < 4; c++) + average.color[c] /= average.n; + } + + babl_process (babl_fish (average_format, format), average.color, color, 1); +} + +} /* extern "C" */ |