[gimp/gimp-2-10] Issue #2372 - Reduced quality of the Parametric brush in 2.10
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] Issue #2372 - Reduced quality of the Parametric brush in 2.10
- Date: Wed, 6 Feb 2019 19:31:42 +0000 (UTC)
commit d39634a91432bdc7a6e7a96b6bdaa4b8efaecdd1
Author: Ell <ell_se yahoo com>
Date: Wed Feb 6 14:15:47 2019 -0500
Issue #2372 - Reduced quality of the Parametric brush in 2.10
Promote the precision of generated brushes to 32-bit float, and
modify brush preview generation, and gimpbrushcore-loops, to handle
float brushes. This avoids posterization in large brushes.
Note that non-generated brushes are still uint8.
(cherry picked from commit 8ef1113dee378e66182e13dcf924b32b7dd816c4)
app/core/gimpbrush.c | 15 +-
app/core/gimpbrushgenerated.c | 24 +-
app/paint/gimpbrushcore-kernels.h | 126 ++++++---
app/paint/gimpbrushcore-loops.cc | 554 +++++++++++++++++++++++++-------------
4 files changed, 481 insertions(+), 238 deletions(-)
---
diff --git a/app/core/gimpbrush.c b/app/core/gimpbrush.c
index b4791b70c4..88c3691cef 100644
--- a/app/core/gimpbrush.c
+++ b/app/core/gimpbrush.c
@@ -270,6 +270,7 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
GimpTempBuf *return_buf = NULL;
gint mask_width;
gint mask_height;
+ guchar *mask_data;
guchar *mask;
guchar *buf;
gint x, y;
@@ -327,12 +328,18 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
babl_format ("R'G'B'A u8"));
gimp_temp_buf_data_clear (return_buf);
- mask = gimp_temp_buf_get_data (mask_buf);
+ mask = mask_data = gimp_temp_buf_lock (mask_buf, babl_format ("Y u8"),
+ GEGL_ACCESS_READ);
buf = gimp_temp_buf_get_data (return_buf);
if (pixmap_buf)
{
- guchar *pixmap = gimp_temp_buf_get_data (pixmap_buf);
+ guchar *pixmap_data;
+ guchar *pixmap;
+
+ pixmap = pixmap_data = gimp_temp_buf_lock (pixmap_buf,
+ babl_format ("R'G'B' u8"),
+ GEGL_ACCESS_READ);
for (y = 0; y < mask_height; y++)
{
@@ -344,6 +351,8 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
*buf++ = *mask++;
}
}
+
+ gimp_temp_buf_unlock (pixmap_buf, pixmap_data);
}
else
{
@@ -359,6 +368,8 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
}
}
+ gimp_temp_buf_unlock (mask_buf, mask_data);
+
if (scaled)
{
gimp_temp_buf_unref ((GimpTempBuf *) mask_buf);
diff --git a/app/core/gimpbrushgenerated.c b/app/core/gimpbrushgenerated.c
index 0284bddef6..dacdaa8683 100644
--- a/app/core/gimpbrushgenerated.c
+++ b/app/core/gimpbrushgenerated.c
@@ -405,11 +405,11 @@ gauss (gdouble f)
}
/* set up lookup table */
-static guchar *
+static gfloat *
gimp_brush_generated_calc_lut (gdouble radius,
gdouble hardness)
{
- guchar *lookup;
+ gfloat *lookup;
gint length;
gint x;
gdouble d;
@@ -419,7 +419,7 @@ gimp_brush_generated_calc_lut (gdouble radius,
length = OVERSAMPLING * ceil (1 + sqrt (2 * SQR (ceil (radius + 1.0))));
- lookup = g_malloc (length);
+ lookup = gegl_scratch_new (gfloat, length);
sum = 0.0;
if ((1.0 - hardness) < 0.0000004)
@@ -449,12 +449,12 @@ gimp_brush_generated_calc_lut (gdouble radius,
buffer[x % OVERSAMPLING] = gauss (pow (d / radius, exponent));
sum += buffer[x % OVERSAMPLING];
- lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING));
+ lookup[x++] = sum / OVERSAMPLING;
}
while (x < length)
{
- lookup[x++] = 0;
+ lookup[x++] = 0.0f;
}
return lookup;
@@ -472,9 +472,9 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush,
GimpVector2 *xaxis,
GimpVector2 *yaxis)
{
- guchar *centerp;
- guchar *lookup;
- guchar a;
+ gfloat *centerp;
+ gfloat *lookup;
+ gfloat a;
gint x, y;
gdouble c, s, cs, ss;
GimpVector2 x_axis;
@@ -497,12 +497,12 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush,
&s, &c, &x_axis, &y_axis);
mask = gimp_temp_buf_new (width, height,
- babl_format ("Y u8"));
+ babl_format ("Y float"));
half_width = width / 2;
half_height = height / 2;
- centerp = gimp_temp_buf_get_data (mask) +
+ centerp = (gfloat *) gimp_temp_buf_get_data (mask) +
half_height * width + half_width;
lookup = gimp_brush_generated_calc_lut (radius, hardness);
@@ -553,7 +553,7 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush,
if (d < radius + 1)
a = lookup[(gint) RINT (d * OVERSAMPLING)];
else
- a = 0;
+ a = 0.0f;
centerp[y * width + x] = a;
@@ -562,7 +562,7 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush,
}
}
- g_free (lookup);
+ gegl_scratch_free (lookup);
if (xaxis)
*xaxis = x_axis;
diff --git a/app/paint/gimpbrushcore-kernels.h b/app/paint/gimpbrushcore-kernels.h
index a9ae0768d0..32503ef56e 100644
--- a/app/paint/gimpbrushcore-kernels.h
+++ b/app/paint/gimpbrushcore-kernels.h
@@ -7,50 +7,110 @@
#ifndef __GIMP_BRUSH_CORE_KERNELS_H__
#define __GIMP_BRUSH_CORE_KERNELS_H__
+
#define KERNEL_WIDTH 3
#define KERNEL_HEIGHT 3
#define KERNEL_SUBSAMPLE 4
-#define KERNEL_SUM 256
-/* Brush pixel subsampling kernels */
-static const int subsample[5][5][9] =
+#ifdef __cplusplus
+
+template <class T>
+struct Kernel;
+
+template <>
+struct Kernel<guchar>
{
+ using value_type = guchar;
+ using kernel_type = guint;
+ using accum_type = gulong;
+
+ static constexpr kernel_type
+ coeff (kernel_type x)
{
- { 64, 64, 0, 64, 64, 0, 0, 0, 0, },
- { 25, 103, 0, 25, 103, 0, 0, 0, 0, },
- { 0, 128, 0, 0, 128, 0, 0, 0, 0, },
- { 0, 103, 25, 0, 103, 25, 0, 0, 0, },
- { 0, 64, 64, 0, 64, 64, 0, 0, 0, }
- },
- {
- { 25, 25, 0, 103, 103, 0, 0, 0, 0, },
- { 6, 44, 0, 44, 162, 0, 0, 0, 0, },
- { 0, 50, 0, 0, 206, 0, 0, 0, 0, },
- { 0, 44, 6, 0, 162, 44, 0, 0, 0, },
- { 0, 25, 25, 0, 103, 103, 0, 0, 0, }
- },
+ return x;
+ }
+
+ static constexpr value_type
+ round (accum_type x)
{
- { 0, 0, 0, 128, 128, 0, 0, 0, 0, },
- { 0, 0, 0, 50, 206, 0, 0, 0, 0, },
- { 0, 0, 0, 0, 256, 0, 0, 0, 0, },
- { 0, 0, 0, 0, 206, 50, 0, 0, 0, },
- { 0, 0, 0, 0, 128, 128, 0, 0, 0, }
- },
+ return (x + 127) / 256;
+ }
+};
+
+template <>
+struct Kernel<gfloat>
+{
+ using value_type = gfloat;
+ using kernel_type = gfloat;
+ using accum_type = gfloat;
+
+ static constexpr kernel_type
+ coeff (kernel_type x)
{
- { 0, 0, 0, 103, 103, 0, 25, 25, 0, },
- { 0, 0, 0, 44, 162, 0, 6, 44, 0, },
- { 0, 0, 0, 0, 206, 0, 0, 50, 0, },
- { 0, 0, 0, 0, 162, 44, 0, 44, 6, },
- { 0, 0, 0, 0, 103, 103, 0, 25, 25, }
- },
+ return x / 256.0f;
+ }
+
+ static constexpr value_type
+ round (accum_type x)
{
- { 0, 0, 0, 64, 64, 0, 64, 64, 0, },
- { 0, 0, 0, 25, 103, 0, 25, 103, 0, },
- { 0, 0, 0, 0, 128, 0, 0, 128, 0, },
- { 0, 0, 0, 0, 103, 25, 0, 103, 25, },
- { 0, 0, 0, 0, 64, 64, 0, 64, 64, }
+ return x;
}
};
+
+/* Brush pixel subsampling kernels */
+template <class T>
+struct Subsample : Kernel<T>
+{
+ #define C(x) (Subsample::coeff (x))
+
+ static constexpr typename Subsample::kernel_type kernel[5][5][9] =
+ {
+ {
+ { C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), C( 0), C( 0), C( 0), },
+ { C( 25), C(103), C( 0), C( 25), C(103), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C(128), C( 0), C( 0), C(128), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C(103), C( 25), C( 0), C(103), C( 25), C( 0), C( 0), C( 0), },
+ { C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), C( 0), C( 0), }
+ },
+ {
+ { C( 25), C( 25), C( 0), C(103), C(103), C( 0), C( 0), C( 0), C( 0), },
+ { C( 6), C( 44), C( 0), C( 44), C(162), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C( 50), C( 0), C( 0), C(206), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C( 44), C( 6), C( 0), C(162), C( 44), C( 0), C( 0), C( 0), },
+ { C( 0), C( 25), C( 25), C( 0), C(103), C(103), C( 0), C( 0), C( 0), }
+ },
+ {
+ { C( 0), C( 0), C( 0), C(128), C(128), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C( 0), C( 0), C( 50), C(206), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(256), C( 0), C( 0), C( 0), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(206), C( 50), C( 0), C( 0), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(128), C(128), C( 0), C( 0), C( 0), }
+ },
+ {
+ { C( 0), C( 0), C( 0), C(103), C(103), C( 0), C( 25), C( 25), C( 0), },
+ { C( 0), C( 0), C( 0), C( 44), C(162), C( 0), C( 6), C( 44), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(206), C( 0), C( 0), C( 50), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(162), C( 44), C( 0), C( 44), C( 6), },
+ { C( 0), C( 0), C( 0), C( 0), C(103), C(103), C( 0), C( 25), C( 25), }
+ },
+ {
+ { C( 0), C( 0), C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), },
+ { C( 0), C( 0), C( 0), C( 25), C(103), C( 0), C( 25), C(103), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(128), C( 0), C( 0), C(128), C( 0), },
+ { C( 0), C( 0), C( 0), C( 0), C(103), C( 25), C( 0), C(103), C( 25), },
+ { C( 0), C( 0), C( 0), C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), }
+ }
+ };
+
+ #undef C
+};
+
+template <class T>
+constexpr typename Subsample<T>::kernel_type Subsample<T>::kernel[5][5][9];
+
+#endif /* __cplusplus */
+
+
#endif /* __GIMP_BRUSH_CORE_KERNELS_H__ */
diff --git a/app/paint/gimpbrushcore-loops.cc b/app/paint/gimpbrushcore-loops.cc
index c0b7d6a120..adf866ba23 100644
--- a/app/paint/gimpbrushcore-loops.cc
+++ b/app/paint/gimpbrushcore-loops.cc
@@ -33,28 +33,141 @@ extern "C"
#include "gimpbrushcore.h"
#include "gimpbrushcore-loops.h"
+
+} /* extern "C" */
+
#include "gimpbrushcore-kernels.h"
#define PIXELS_PER_THREAD \
(/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
+#define EPSILON 1e-6
+
+template <class T>
static inline void
-rotate_pointers (gulong **p,
- guint32 n)
+rotate_pointers (T **p,
+ gint n)
{
- guint32 i;
- gulong *tmp;
+ T *tmp;
+ gint i;
tmp = p[0];
- for (i = 0; i < n-1; i++)
- p[i] = p[i+1];
+ for (i = 0; i < n - 1; i++)
+ p[i] = p[i + 1];
p[i] = tmp;
}
+template <class T>
+static void
+gimp_brush_core_subsample_mask_impl (const GimpTempBuf *mask,
+ GimpTempBuf *dest,
+ gint dest_offset_x,
+ gint dest_offset_y,
+ gint index1,
+ gint index2)
+{
+ using value_type = typename Subsample<T>::value_type;
+ using kernel_type = typename Subsample<T>::kernel_type;
+ using accum_type = typename Subsample<T>::accum_type;
+
+ Subsample<T> subsample;
+ const kernel_type *kernel = subsample.kernel[index2][index1];
+ gint mask_width = gimp_temp_buf_get_width (mask);
+ gint mask_height = gimp_temp_buf_get_height (mask);
+ gint dest_width = gimp_temp_buf_get_width (dest);
+ gint dest_height = gimp_temp_buf_get_height (dest);
+
+ gegl_parallel_distribute_range (
+ mask_height, PIXELS_PER_THREAD / mask_width,
+ [=] (gint y, gint height)
+ {
+ const value_type *m;
+ value_type *d;
+ const kernel_type *k;
+ gint y0;
+ gint i, j;
+ gint r, s;
+ gint offs;
+ accum_type *accum[KERNEL_HEIGHT];
+
+ /* Allocate and initialize the accum buffer */
+ for (i = 0; i < KERNEL_HEIGHT ; i++)
+ accum[i] = gegl_scratch_new0 (accum_type, dest_width + 1);
+
+ y0 = MAX (y - (KERNEL_HEIGHT - 1), 0);
+
+ m = (const value_type *) 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 = (value_type *) gimp_temp_buf_get_data (dest) +
+ (i + dest_offset_y) * dest_width;
+ for (j = 0; j < dest_width; j++)
+ *d++ = subsample.round (accum[0][j]);
+
+ rotate_pointers (accum, KERNEL_HEIGHT);
+
+ memset (accum[KERNEL_HEIGHT - 1], 0,
+ sizeof (accum_type) * 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 = (value_type *) gimp_temp_buf_get_data (dest) +
+ (i + dest_offset_y) * dest_width;
+ for (j = 0; j < dest_width; j++)
+ *d++ = subsample.round (accum[0][j]);
+
+ rotate_pointers (accum, KERNEL_HEIGHT);
+ i++;
+ }
+ }
+
+ for (i = KERNEL_HEIGHT - 1; i >= 0; i--)
+ gegl_scratch_free (accum[i]);
+ });
+}
+
const GimpTempBuf *
gimp_brush_core_subsample_mask (GimpBrushCore *core,
const GimpTempBuf *mask,
@@ -62,16 +175,14 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core,
gdouble y)
{
GimpTempBuf *dest;
+ const Babl *mask_format;
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));
@@ -79,7 +190,6 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core,
left = y - floor (y);
index2 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1));
-
if ((mask_width % 2) == 0)
{
index1 += KERNEL_SUBSAMPLE >> 1;
@@ -102,8 +212,6 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core,
}
}
- kernel = subsample[index2][index1];
-
if (mask == core->last_subsample_brush_mask &&
! core->subsample_cache_invalid)
{
@@ -122,150 +230,93 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core,
core->subsample_cache_invalid = FALSE;
}
+ mask_format = gimp_temp_buf_get_format (mask);
+
dest = gimp_temp_buf_new (mask_width + 2,
mask_height + 2,
- gimp_temp_buf_get_format (mask));
+ mask_format);
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)
+ if (mask_format == babl_format ("Y u8"))
{
- 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]);
- });
+ gimp_brush_core_subsample_mask_impl<guchar> (mask, dest,
+ dest_offset_x, dest_offset_y,
+ index1, index2);
+ }
+ else if (mask_format == babl_format ("Y float"))
+ {
+ gimp_brush_core_subsample_mask_impl<gfloat> (mask, dest,
+ dest_offset_x, dest_offset_y,
+ index1, index2);
+ }
+ else
+ {
+ g_warn_if_reached ();
+ }
return dest;
}
-/* #define FANCY_PRESSURE */
-
-const GimpTempBuf *
-gimp_brush_core_pressurize_mask (GimpBrushCore *core,
- const GimpTempBuf *brush_mask,
- gdouble x,
- gdouble y,
- gdouble pressure)
+/* The simple pressure profile
+ *
+ * It is: I'(I) = MIN (2 * pressure * I, 1)
+ */
+class SimplePressure
{
- static guchar mapi[256];
- const GimpTempBuf *subsample_mask;
- gint i;
+ gfloat scale;
- /* Get the raw subsampled mask */
- subsample_mask = gimp_brush_core_subsample_mask (core,
- brush_mask,
- x, y);
+public:
+ SimplePressure (gdouble pressure)
+ {
+ scale = 2.0 * pressure;
+ }
- /* Special case pressure = 0.5 */
- if ((gint) (pressure * 100 + 0.5) == 50)
- return subsample_mask;
+ guchar
+ operator () (guchar x) const
+ {
+ gint v = RINT (scale * x);
- g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref);
+ return MIN (v, 255);
+ }
- 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);
+ gfloat
+ operator () (gfloat x) const
+ {
+ gfloat v = scale * x;
-#ifdef FANCY_PRESSURE
+ return MIN (v, 1.0f);
+ }
+
+ template <class T>
+ T
+ operator () (T x) const = delete;
+};
- /* 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
- *
- * | / --
- * | / /
- * / / |
- * -- / |
- */
+/* The fancy 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
+ *
+ * | / --
+ * | / /
+ * / / |
+ * -- / |
+ */
+class FancyPressure
+{
+ gfloat map[257];
+
+public:
+ FancyPressure (gdouble pressure)
{
- gdouble map[256];
- gdouble ds, s, c;
+ gdouble ds, s, c;
+ gint i;
ds = (pressure - 0.5) * (20.0 / 256.0);
s = 0;
@@ -281,7 +332,7 @@ gimp_brush_core_pressurize_mask (GimpBrushCore *core,
}
for (i = 0; i < 256; i++)
- mapi[i] = (gint) (255 * map[i] / map[255]);
+ map[i] = map[i] / map[255];
}
else
{
@@ -294,54 +345,183 @@ gimp_brush_core_pressurize_mask (GimpBrushCore *core,
c += s * ds;
}
- for (i = 0; i < 256; i++)
- mapi[i] = (gint) (255 * (1 - map[i] / map[0]));
+ for (i = 255; i >= 0; i--)
+ map[i] = 1.0f - map[i] / map[0];
}
+
+ map[256] = map[255];
}
-#else /* ! FANCY_PRESSURE */
+ guchar
+ operator () (guchar x) const
+ {
+ return RINT (255.0f * map[x]);
+ }
+ gfloat
+ operator () (gfloat x) const
{
- gdouble j, k;
+ gint i;
+ gfloat f;
- j = pressure + pressure;
- k = 0;
+ x *= 255.0f;
- for (i = 0; i < 256; i++)
- {
- if (k > 255)
- mapi[i] = 255;
- else
- mapi[i] = (guchar) k;
+ i = floorf (x);
+ f = x - i;
- k += j;
- }
+ return map[i] + (map[i + 1] - map[i]) * f;
}
-#endif /* FANCY_PRESSURE */
+ template <class T>
+ T
+ operator () (T x) const = delete;
+};
+
+template <class T>
+class CachedPressure
+{
+ T map[T (~0) + 1];
- /* Now convert the brush */
+public:
+ template <class Pressure>
+ CachedPressure (Pressure pressure)
+ {
+ gint i;
+ for (i = 0; i < G_N_ELEMENTS (map); i++)
+ map[i] = pressure (T (i));
+ }
+
+ T
+ operator () (T x) const
+ {
+ return map[x];
+ }
+
+ template <class U>
+ U
+ operator () (U x) const = delete;
+};
+
+template <class T,
+ class Pressure>
+void
+gimp_brush_core_pressurize_mask_impl (const GimpTempBuf *mask,
+ GimpTempBuf *dest,
+ Pressure pressure)
+{
gegl_parallel_distribute_range (
- gimp_temp_buf_get_width (subsample_mask) *
- gimp_temp_buf_get_height (subsample_mask),
+ gimp_temp_buf_get_width (mask) * gimp_temp_buf_get_height (mask),
PIXELS_PER_THREAD,
[=] (gint offset, gint size)
{
- const guchar *source;
- guchar *dest;
- gint i;
+ const T *m;
+ T *d;
+ gint i;
- source = gimp_temp_buf_get_data (subsample_mask) + offset;
- dest = gimp_temp_buf_get_data (core->pressure_brush) + offset;
+ m = (const T *) gimp_temp_buf_get_data (mask) + offset;
+ d = ( T *) gimp_temp_buf_get_data (dest) + offset;
for (i = 0; i < size; i++)
- *dest++ = mapi[(*source++)];
+ *d++ = pressure (*m++);
});
+}
+
+/* #define FANCY_PRESSURE */
+
+const GimpTempBuf *
+gimp_brush_core_pressurize_mask (GimpBrushCore *core,
+ const GimpTempBuf *brush_mask,
+ gdouble x,
+ gdouble y,
+ gdouble pressure)
+{
+ const GimpTempBuf *subsample_mask;
+ const Babl *subsample_mask_format;
+ 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 (fabs (pressure - 0.5) <= EPSILON)
+ return subsample_mask;
+
+ g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref);
+
+ subsample_mask_format = gimp_temp_buf_get_format (subsample_mask);
+
+ core->pressure_brush =
+ gimp_temp_buf_new (gimp_temp_buf_get_width (brush_mask) + 2,
+ gimp_temp_buf_get_height (brush_mask) + 2,
+ subsample_mask_format);
+ gimp_temp_buf_data_clear (core->pressure_brush);
+
+#ifdef FANCY_PRESSURE
+ using Pressure = FancyPressure;
+#else
+ using Pressure = SimplePressure;
+#endif
+
+ if (subsample_mask_format == babl_format ("Y u8"))
+ {
+ gimp_brush_core_pressurize_mask_impl<guchar> (subsample_mask,
+ core->pressure_brush,
+ CachedPressure<guchar> (
+ Pressure (pressure)));
+ }
+ else if (subsample_mask_format == babl_format ("Y float"))
+ {
+ gimp_brush_core_pressurize_mask_impl<gfloat> (subsample_mask,
+ core->pressure_brush,
+ Pressure (pressure));
+ }
+ else
+ {
+ g_warn_if_reached ();
+ }
return core->pressure_brush;
}
+template <class T>
+static void
+gimp_brush_core_solidify_mask_impl (const GimpTempBuf *brush_mask,
+ GimpTempBuf *dest,
+ gint dest_offset_x,
+ gint dest_offset_y)
+{
+ gint brush_mask_width = gimp_temp_buf_get_width (brush_mask);
+ gint brush_mask_height = gimp_temp_buf_get_height (brush_mask);
+
+ gegl_parallel_distribute_area (
+ GEGL_RECTANGLE (0, 0, brush_mask_width, brush_mask_height),
+ PIXELS_PER_THREAD,
+ [=] (const GeglRectangle *area)
+ {
+ const T *m;
+ gfloat *d;
+ gint i, j;
+
+ m = (const T *) 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;
+ }
+ });
+}
+
const GimpTempBuf *
gimp_brush_core_solidify_mask (GimpBrushCore *core,
const GimpTempBuf *brush_mask,
@@ -349,6 +529,7 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
gdouble y)
{
GimpTempBuf *dest;
+ const Babl *brush_mask_format;
gint dest_offset_x = 0;
gint dest_offset_y = 0;
gint brush_mask_width = gimp_temp_buf_get_width (brush_mask);
@@ -356,8 +537,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
if ((brush_mask_width % 2) == 0)
{
- while (x < 0)
- x += brush_mask_width;
+ if (x < 0.0)
+ x = fmod (x, brush_mask_width) + brush_mask_width;
if ((x - floor (x)) >= 0.5)
dest_offset_x++;
@@ -365,8 +546,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
if ((brush_mask_height % 2) == 0)
{
- while (y < 0)
- y += brush_mask_height;
+ if (y < 0.0)
+ y = fmod (y, brush_mask_height) + brush_mask_height;
if ((y - floor (y)) >= 0.5)
dest_offset_y++;
@@ -390,6 +571,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
core->solid_cache_invalid = FALSE;
}
+ brush_mask_format = gimp_temp_buf_get_format (brush_mask);
+
dest = gimp_temp_buf_new (brush_mask_width + 2,
brush_mask_height + 2,
babl_format ("Y float"));
@@ -397,32 +580,21 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
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;
- }
- });
+ if (brush_mask_format == babl_format ("Y u8"))
+ {
+ gimp_brush_core_solidify_mask_impl<guchar> (brush_mask, dest,
+ dest_offset_x, dest_offset_y);
+ }
+ else if (brush_mask_format == babl_format ("Y float"))
+ {
+ gimp_brush_core_solidify_mask_impl<gfloat> (brush_mask, dest,
+ dest_offset_x, dest_offset_y);
+ }
+ else
+ {
+ g_warn_if_reached ();
+ }
return dest;
}
-
-} /* extern "C" */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]