Files
gimp/app/paint/gimpbrushcore-loops.cc
Ell 2736cee577 app: remove gimp_parallel_distribute(); use gegl_parallel_distribute()
The parallel_distribute() family of functions has been migrated to
GEGL.  Remove the gimp_parallel_distribute() functions from
gimp-parallel, and replace all uses of these functions with the
corresponding gegl_parallel_distrubte() functions.
2018-11-14 10:52:06 -05:00

429 lines
11 KiB
C++

/* GIMP - The GNU Image Manipulation Program
* 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 <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpmath/gimpmath.h"
extern "C"
{
#include "paint-types.h"
#include "core/gimptempbuf.h"
#include "gimpbrushcore.h"
#include "gimpbrushcore-loops.h"
#include "gimpbrushcore-kernels.h"
#define PIXELS_PER_THREAD \
(/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
static inline void
rotate_pointers (gulong **p,
guint32 n)
{
guint32 i;
gulong *tmp;
tmp = p[0];
for (i = 0; i < n-1; i++)
p[i] = p[i+1];
p[i] = tmp;
}
const GimpTempBuf *
gimp_brush_core_subsample_mask (GimpBrushCore *core,
const GimpTempBuf *mask,
gdouble x,
gdouble y)
{
GimpTempBuf *dest;
gdouble left;
gint index1;
gint index2;
gint dest_offset_x = 0;
gint dest_offset_y = 0;
const gint *kernel;
gint mask_width = gimp_temp_buf_get_width (mask);
gint mask_height = gimp_temp_buf_get_height (mask);
gint dest_width;
gint dest_height;
left = x - floor (x);
index1 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1));
left = y - floor (y);
index2 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1));
if ((mask_width % 2) == 0)
{
index1 += KERNEL_SUBSAMPLE >> 1;
if (index1 > KERNEL_SUBSAMPLE)
{
index1 -= KERNEL_SUBSAMPLE + 1;
dest_offset_x = 1;
}
}
if ((mask_height % 2) == 0)
{
index2 += KERNEL_SUBSAMPLE >> 1;
if (index2 > KERNEL_SUBSAMPLE)
{
index2 -= KERNEL_SUBSAMPLE + 1;
dest_offset_y = 1;
}
}
kernel = subsample[index2][index1];
if (mask == core->last_subsample_brush_mask &&
! core->subsample_cache_invalid)
{
if (core->subsample_brushes[index2][index1])
return core->subsample_brushes[index2][index1];
}
else
{
gint i, j;
for (i = 0; i < KERNEL_SUBSAMPLE + 1; i++)
for (j = 0; j < KERNEL_SUBSAMPLE + 1; j++)
g_clear_pointer (&core->subsample_brushes[i][j], gimp_temp_buf_unref);
core->last_subsample_brush_mask = mask;
core->subsample_cache_invalid = FALSE;
}
dest = gimp_temp_buf_new (mask_width + 2,
mask_height + 2,
gimp_temp_buf_get_format (mask));
gimp_temp_buf_data_clear (dest);
dest_width = gimp_temp_buf_get_width (dest);
dest_height = gimp_temp_buf_get_height (dest);
core->subsample_brushes[index2][index1] = dest;
gegl_parallel_distribute_range (
mask_height, PIXELS_PER_THREAD / mask_width,
[=] (gint y, gint height)
{
const guchar *m;
guchar *d;
const gint *k;
gint y0;
gint i, j;
gint r, s;
gint offs;
gulong *accum[KERNEL_HEIGHT];
/* Allocate and initialize the accum buffer */
for (i = 0; i < KERNEL_HEIGHT ; i++)
accum[i] = g_new0 (gulong, dest_width + 1);
y0 = MAX (y - (KERNEL_HEIGHT - 1), 0);
m = gimp_temp_buf_get_data (mask) + y0 * mask_width;
for (i = y0; i < y; i++)
{
for (j = 0; j < mask_width; j++)
{
k = kernel + KERNEL_WIDTH * (y - i);
for (r = y - i; r < KERNEL_HEIGHT; r++)
{
offs = j + dest_offset_x;
s = KERNEL_WIDTH;
while (s--)
accum[r][offs++] += *m * *k++;
}
m++;
}
rotate_pointers (accum, KERNEL_HEIGHT);
}
for (i = y; i < y + height; i++)
{
for (j = 0; j < mask_width; j++)
{
k = kernel;
for (r = 0; r < KERNEL_HEIGHT; r++)
{
offs = j + dest_offset_x;
s = KERNEL_WIDTH;
while (s--)
accum[r][offs++] += *m * *k++;
}
m++;
}
/* store the accum buffer into the destination mask */
d = gimp_temp_buf_get_data (dest) + (i + dest_offset_y) * dest_width;
for (j = 0; j < dest_width; j++)
*d++ = (accum[0][j] + 127) / KERNEL_SUM;
rotate_pointers (accum, KERNEL_HEIGHT);
memset (accum[KERNEL_HEIGHT - 1], 0, sizeof (gulong) * dest_width);
}
if (y + height == mask_height)
{
/* store the rest of the accum buffer into the dest mask */
while (i + dest_offset_y < dest_height)
{
d = gimp_temp_buf_get_data (dest) + (i + dest_offset_y) * dest_width;
for (j = 0; j < dest_width; j++)
*d++ = (accum[0][j] + (KERNEL_SUM / 2)) / KERNEL_SUM;
rotate_pointers (accum, KERNEL_HEIGHT);
i++;
}
}
for (i = 0; i < KERNEL_HEIGHT ; i++)
g_free (accum[i]);
});
return dest;
}
/* #define FANCY_PRESSURE */
const GimpTempBuf *
gimp_brush_core_pressurize_mask (GimpBrushCore *core,
const GimpTempBuf *brush_mask,
gdouble x,
gdouble y,
gdouble pressure)
{
static guchar mapi[256];
const GimpTempBuf *subsample_mask;
gint i;
/* Get the raw subsampled mask */
subsample_mask = gimp_brush_core_subsample_mask (core,
brush_mask,
x, y);
/* Special case pressure = 0.5 */
if ((gint) (pressure * 100 + 0.5) == 50)
return subsample_mask;
g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref);
core->pressure_brush =
gimp_temp_buf_new (gimp_temp_buf_get_width (brush_mask) + 2,
gimp_temp_buf_get_height (brush_mask) + 2,
gimp_temp_buf_get_format (brush_mask));
gimp_temp_buf_data_clear (core->pressure_brush);
#ifdef FANCY_PRESSURE
/* Create the pressure profile
*
* It is: I'(I) = tanh (20 * (pressure - 0.5) * I) : pressure > 0.5
* I'(I) = 1 - tanh (20 * (0.5 - pressure) * (1 - I)) : pressure < 0.5
*
* It looks like:
*
* low pressure medium pressure high pressure
*
* | / --
* | / /
* / / |
* -- / |
*/
{
gdouble map[256];
gdouble ds, s, c;
ds = (pressure - 0.5) * (20.0 / 256.0);
s = 0;
c = 1.0;
if (ds > 0)
{
for (i = 0; i < 256; i++)
{
map[i] = s / c;
s += c * ds;
c += s * ds;
}
for (i = 0; i < 256; i++)
mapi[i] = (gint) (255 * map[i] / map[255]);
}
else
{
ds = -ds;
for (i = 255; i >= 0; i--)
{
map[i] = s / c;
s += c * ds;
c += s * ds;
}
for (i = 0; i < 256; i++)
mapi[i] = (gint) (255 * (1 - map[i] / map[0]));
}
}
#else /* ! FANCY_PRESSURE */
{
gdouble j, k;
j = pressure + pressure;
k = 0;
for (i = 0; i < 256; i++)
{
if (k > 255)
mapi[i] = 255;
else
mapi[i] = (guchar) k;
k += j;
}
}
#endif /* FANCY_PRESSURE */
/* Now convert the brush */
gegl_parallel_distribute_range (
gimp_temp_buf_get_width (subsample_mask) *
gimp_temp_buf_get_height (subsample_mask),
PIXELS_PER_THREAD,
[=] (gint offset, gint size)
{
const guchar *source;
guchar *dest;
gint i;
source = gimp_temp_buf_get_data (subsample_mask) + offset;
dest = gimp_temp_buf_get_data (core->pressure_brush) + offset;
for (i = 0; i < size; i++)
*dest++ = mapi[(*source++)];
});
return core->pressure_brush;
}
const GimpTempBuf *
gimp_brush_core_solidify_mask (GimpBrushCore *core,
const GimpTempBuf *brush_mask,
gdouble x,
gdouble y)
{
GimpTempBuf *dest;
gint dest_offset_x = 0;
gint dest_offset_y = 0;
gint brush_mask_width = gimp_temp_buf_get_width (brush_mask);
gint brush_mask_height = gimp_temp_buf_get_height (brush_mask);
if ((brush_mask_width % 2) == 0)
{
while (x < 0)
x += brush_mask_width;
if ((x - floor (x)) >= 0.5)
dest_offset_x++;
}
if ((brush_mask_height % 2) == 0)
{
while (y < 0)
y += brush_mask_height;
if ((y - floor (y)) >= 0.5)
dest_offset_y++;
}
if (! core->solid_cache_invalid &&
brush_mask == core->last_solid_brush_mask)
{
if (core->solid_brushes[dest_offset_y][dest_offset_x])
return core->solid_brushes[dest_offset_y][dest_offset_x];
}
else
{
gint i, j;
for (i = 0; i < BRUSH_CORE_SOLID_SUBSAMPLE; i++)
for (j = 0; j < BRUSH_CORE_SOLID_SUBSAMPLE; j++)
g_clear_pointer (&core->solid_brushes[i][j], gimp_temp_buf_unref);
core->last_solid_brush_mask = brush_mask;
core->solid_cache_invalid = FALSE;
}
dest = gimp_temp_buf_new (brush_mask_width + 2,
brush_mask_height + 2,
babl_format ("Y float"));
gimp_temp_buf_data_clear (dest);
core->solid_brushes[dest_offset_y][dest_offset_x] = dest;
gegl_parallel_distribute_area (
GEGL_RECTANGLE (0, 0, brush_mask_width, brush_mask_height),
PIXELS_PER_THREAD,
[=] (const GeglRectangle *area)
{
const guchar *m;
gfloat *d;
gint i, j;
m = gimp_temp_buf_get_data (brush_mask) +
area->y * brush_mask_width + area->x;
d = ((gfloat *) gimp_temp_buf_get_data (dest) +
((dest_offset_y + 1 + area->y) * gimp_temp_buf_get_width (dest) +
(dest_offset_x + 1 + area->x)));
for (i = 0; i < area->height; i++)
{
for (j = 0; j < area->width; j++)
*d++ = (*m++) ? 1.0 : 0.0;
m += brush_mask_width - area->width;
d += brush_mask_width + 2 - area->width;
}
});
return dest;
}
} /* extern "C" */