[gimp/wip/alxsa/mypaint-brush-v2] Implement draw_dab_2 API
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/alxsa/mypaint-brush-v2] Implement draw_dab_2 API
- Date: Sat, 8 Oct 2022 17:47:23 +0000 (UTC)
commit d2ac22c4413ae3d3b68c457cb3961a7fa7771b4a
Author: Alx Sa <cmyk student gmail com>
Date: Sat Oct 8 17:47:22 2022 +0000
Implement draw_dab_2 API
app/core/gimpmybrush-load.c | 26 ++++---
app/core/gimpmybrush.c | 4 +-
app/paint/gimpmybrushcore.c | 2 -
app/paint/gimpmybrushoptions.c | 4 +-
app/paint/gimpmybrushsurface.c | 159 +++++++++++++++++++++++++++++++++--------
5 files changed, 150 insertions(+), 45 deletions(-)
---
diff --git a/app/core/gimpmybrush-load.c b/app/core/gimpmybrush-load.c
index eeca69347d..61e1e32898 100644
--- a/app/core/gimpmybrush-load.c
+++ b/app/core/gimpmybrush-load.c
@@ -139,25 +139,29 @@ gimp_mybrush_load (GimpContext *context,
mypaint_brush_get_base_value (mypaint_brush,
MYPAINT_BRUSH_SETTING_HARDNESS);
- brush->priv->pigment =
+ brush->priv->eraser =
mypaint_brush_get_base_value (mypaint_brush,
- MYPAINT_BRUSH_SETTING_PAINT_MODE);
+ MYPAINT_BRUSH_SETTING_ERASER) > 0.5f;
- brush->priv->posterize =
+ brush->priv->offset_by_random =
mypaint_brush_get_base_value (mypaint_brush,
- MYPAINT_BRUSH_SETTING_POSTERIZE);
+ MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM);
- brush->priv->posterize_num =
+ /* Version 2 MyPaint Brush options */
+ brush->priv->pigment =
mypaint_brush_get_base_value (mypaint_brush,
- MYPAINT_BRUSH_SETTING_POSTERIZE_NUM);
+ MYPAINT_BRUSH_SETTING_PAINT_MODE);
- brush->priv->eraser =
+ brush->priv->posterize =
mypaint_brush_get_base_value (mypaint_brush,
- MYPAINT_BRUSH_SETTING_ERASER) > 0.5f;
+ MYPAINT_BRUSH_SETTING_POSTERIZE);
- brush->priv->offset_by_random =
- mypaint_brush_get_base_value (mypaint_brush,
- MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM);
+ if (! mypaint_brush_is_constant (mypaint_brush, MYPAINT_BRUSH_SETTING_POSTERIZE_NUM))
+ brush->priv->posterize_num =
+ mypaint_brush_get_base_value (mypaint_brush,
+ MYPAINT_BRUSH_SETTING_POSTERIZE_NUM);
+ else
+ brush->priv->posterize_num = 1.0f;
mypaint_brush_unref (mypaint_brush);
diff --git a/app/core/gimpmybrush.c b/app/core/gimpmybrush.c
index 5117a3ead3..1a04bfa76c 100644
--- a/app/core/gimpmybrush.c
+++ b/app/core/gimpmybrush.c
@@ -101,7 +101,7 @@ gimp_mybrush_init (GimpMybrush *brush)
brush->priv->radius = 1.0;
brush->priv->opaque = 1.0;
brush->priv->hardness = 1.0;
- brush->priv->pigment = 0.0;
+ brush->priv->pigment = -0.1;
brush->priv->posterize = 0.0;
brush->priv->posterize_num = 1.0;
brush->priv->eraser = FALSE;
@@ -270,7 +270,7 @@ gimp_mybrush_get_hardness (GimpMybrush *brush)
gdouble
gimp_mybrush_get_pigment (GimpMybrush *brush)
{
- g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), -1.0);
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), -0.1);
return brush->priv->pigment;
}
diff --git a/app/paint/gimpmybrushcore.c b/app/paint/gimpmybrushcore.c
index fcae5a3dab..1c61c0bded 100644
--- a/app/paint/gimpmybrushcore.c
+++ b/app/paint/gimpmybrushcore.c
@@ -241,8 +241,6 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
case GIMP_PAINT_STATE_FINISH:
gimp_symmetry_set_stateful (sym, FALSE);
- /*mypaint_surface_unref (mypaint_surface2_to_surface (
- (MyPaintSurface2 *) mybrush->private->surface));*/
mybrush->private->surface = NULL;
g_list_free_full (mybrush->private->brushes,
diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c
index 2d1d1a57f6..9ef71d50e3 100644
--- a/app/paint/gimpmybrushoptions.c
+++ b/app/paint/gimpmybrushoptions.c
@@ -125,8 +125,8 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PIGMENT,
"pigment",
_("Pigment"),
- NULL,
- -1.0, 1.0, -1.0,
+ _("Enable spectral blending"),
+ -0.1, 1.0, -0.1,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_POSTERIZE,
diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c
index a358ba81ba..b7145b9421 100644
--- a/app/paint/gimpmybrushsurface.c
+++ b/app/paint/gimpmybrushsurface.c
@@ -26,6 +26,10 @@
#include "libgimpmath/gimpmath.h"
#include <cairo.h>
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "fastapprox/fastpow.h"
#include "libgimpcolor/gimpcolor.h"
@@ -90,6 +94,9 @@ void
spectral_to_rgb (float *spectral,
float *rgb_);
+float
+spectral_blend_factor (float x);
+
void
get_color_pixels_accumulate (float *mask,
float *pixel,
@@ -101,7 +108,8 @@ get_color_pixels_accumulate (float *mask,
float paint,
float pixel_weight,
uint16_t sample_interval,
- float random_sample_rate);
+ float random_sample_rate,
+ uint16_t interval_counter);
/* --- Taken from mypaint-tiled-surface.c --- */
static inline float
@@ -318,18 +326,19 @@ get_color_pixels_accumulate (float *mask,
float *sum_g,
float *sum_b,
float *sum_a,
- float pixel_weight,
float paint,
+ float pixel_weight,
uint16_t sample_interval,
- float random_sample_rate)
+ float random_sample_rate,
+ uint16_t interval_counter)
{
- uint16_t interval_counter = 0;
const int random_sample_threshold = (int)(random_sample_rate * G_MAXINT);
float avg_spectral[10] = {0};
float avg_rgb[3] = {*sum_r, *sum_g, *sum_b};
float spec_rgb[3] = {0};
rgb_to_spectral (*sum_r, *sum_g, *sum_b, avg_spectral);
+
if (interval_counter == 0 || rand() < random_sample_threshold)
{
float fac_a, fac_b;
@@ -354,7 +363,8 @@ get_color_pixels_accumulate (float *mask,
spectral);
for (int i = 0; i < 10; i++)
- avg_spectral[i] = fastpow (spectral[i], fac_a) * fastpow (avg_spectral[i], fac_b);
+ avg_spectral[i] =
+ fastpow (spectral[i], fac_a) * fastpow (avg_spectral[i], fac_b);
}
if (paint < 1.0f && pixel[ALPHA] > 0)
@@ -365,13 +375,23 @@ get_color_pixels_accumulate (float *mask,
*sum_a += a;
}
- interval_counter = (interval_counter + 1) % sample_interval;
-
spectral_to_rgb (avg_spectral, spec_rgb);
- *sum_r = (float) spec_rgb[0] * paint + (1.0 - paint) * avg_rgb[0];
- *sum_g = (float) spec_rgb[1] * paint + (1.0 - paint) * avg_rgb[1];
- *sum_b = (float) spec_rgb[2] * paint + (1.0 - paint) * avg_rgb[2];
+ *sum_r = spec_rgb[0] * paint + (1.0 - paint) * avg_rgb[0];
+ *sum_g = spec_rgb[1] * paint + (1.0 - paint) * avg_rgb[1];
+ *sum_b = spec_rgb[2] * paint + (1.0 - paint) * avg_rgb[2];
+}
+
+// Fast sigmoid-like function with constant offsets, used to get a
+// fairly smooth transition between additive and spectral blending.
+float
+spectral_blend_factor (float x)
+{
+ const float ver_fac = 1.65; // vertical compression factor
+ const float hor_fac = 8.0f; // horizontal compression factor
+ const float hor_offs = 3.0f; // horizontal offset (slightly left of center)
+ const float b = x * hor_fac - hor_offs;
+ return 0.5 + b / (1 + fabsf(b) * ver_fac);
}
/* -- end mypaint code */
@@ -836,16 +856,79 @@ gimp_mypaint_surface_draw_dab_2 (MyPaintSurface2 *base_surface,
g = pixel[GREEN];
b = pixel[BLUE];
- if (a > 0.0f)
+ if (paint < 0.0f)
{
- /* By definition the ratio between each color[] and pixel[] component in a
non-pre-multipled blend always sums to 1.0f.
- * Originally this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha *
(1.0f - alpha)) / a",
- * instead we only calculate the cheaper term. */
- float src_term = (alpha * color_a) / a;
- float dst_term = 1.0f - src_term;
- r = color_r * src_term + r * dst_term;
- g = color_g * src_term + g * dst_term;
- b = color_b * src_term + b * dst_term;
+ if (a > 0.0f)
+ {
+ /* By definition the ratio between each color[] and pixel[] component in a
non-pre-multipled blend always sums to 1.0f.
+ * Originally this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha
* (1.0f - alpha)) / a",
+ * instead we only calculate the cheaper term. */
+ float src_term = (alpha * color_a) / a;
+ float dst_term = 1.0f - src_term;
+ r = color_r * src_term + r * dst_term;
+ g = color_g * src_term + g * dst_term;
+ b = color_b * src_term + b * dst_term;
+ }
+ }
+ else
+ {
+ if (a > 0.0f)
+ {
+ float spectral_a[10] = {0};
+ float spectral_b[10] = {0};
+ float spectral_result[10] = {0};
+ float rgb[3] = {0};
+ float rgb_result[3] = {0};
+ float src_term = alpha * MAX (normal_mode, 1.5);
+ float dst_term = 1.0f - src_term;
+ float src_term2 = src_term * color_a;
+ float out_term = src_term2 + dst_term * pixel[ALPHA];
+ float fac_a, fac_b;
+ float spectral_factor, additive_factor;
+
+ rgb_to_spectral (color_r, color_g, color_b, spectral_a);
+
+ spectral_factor =
+ CLAMP (spectral_blend_factor (pixel[ALPHA]), 0.0f, 1.0f);
+ additive_factor = 1.0 - spectral_factor;
+ if (additive_factor)
+ {
+ rgb[0] = src_term2 * color_r + dst_term * r;
+ rgb[1] = src_term2 * color_g + dst_term * g;
+ rgb[2] = src_term2 * color_b + dst_term * b;
+ }
+
+ if (spectral_factor && pixel[ALPHA] != 0)
+ {
+ /* Convert straightened tile pixel color to a spectral */
+ rgb_to_spectral (r / pixel[ALPHA],
+ g / pixel[ALPHA],
+ b / pixel[ALPHA],
+ spectral_b);
+
+ fac_a = src_term / (src_term + dst_term * pixel[ALPHA]);
+ fac_a *= color_a;
+ fac_b = 1.0 - fac_a;
+
+ /* Mix input and tile pixel colors using WGM */
+ for (int i = 0; i < 10; i++) {
+ spectral_result[i] =
+ fastpow (spectral_a[i], fac_a) * fastpow (spectral_b[i], fac_b);
+ }
+
+ /* Convert back to RGB */
+ spectral_to_rgb (spectral_result, rgb_result);
+
+ for (int i = 0; i < 3; i++)
+ rgb[i] = (additive_factor * rgb[i]) +
+ (spectral_factor * rgb_result[i] * out_term);
+ }
+
+ pixel[ALPHA] = out_term;
+ r = rgb[0];
+ g = rgb[1];
+ b = rgb[2];
+ }
}
if (colorize > 0.0f && base_alpha > 0.0f)
@@ -873,6 +956,19 @@ gimp_mypaint_surface_draw_dab_2 (MyPaintSurface2 *base_surface,
}
}
+ if (posterize > 0.0f)
+ {
+ float post_r = ROUND (r * posterize_num) / posterize_num;
+ float post_g = ROUND (g * posterize_num) / posterize_num;
+ float post_b = ROUND (b * posterize_num) / posterize_num;
+ float src_term = opaque * posterize;
+ float dst_term = 1 - src_term;
+
+ r = src_term * post_r + dst_term * r;
+ g = src_term * post_g + dst_term * g;
+ b = src_term * post_b + dst_term * b;
+ }
+
if (surface->options->no_erasing)
a = MAX (a, pixel[ALPHA]);
@@ -988,12 +1084,16 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
float *pixel = (float *)iter->items[0].data;
float *mask;
int iy, ix;
+ uint16_t interval_counter = 0;
if (surface->paint_mask)
mask = iter->items[1].data;
else
mask = NULL;
+ #ifdef _OPENMP
+ #pragma omp parallel for schedule(static)
+ #endif
for (iy = iter->items[0].roi.y; iy < iter->items[0].roi.y + iter->items[0].roi.height; iy++)
{
float yy = (iy + 0.5f - y);
@@ -1025,7 +1125,10 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
#endif
get_color_pixels_accumulate (mask, pixel, &sum_weight,
&sum_r, &sum_g, &sum_b, &sum_a, paint,
- pixel_weight, sample_interval, random_sample_rate);
+ pixel_weight, sample_interval,
+ random_sample_rate, interval_counter);
+
+ interval_counter = (interval_counter + 1) % sample_interval;
}
pixel += 4;
if (mask)
@@ -1036,22 +1139,22 @@ gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
if (sum_a > 0.0f && sum_weight > 0.0f)
{
+ float demul;
+ sum_a /= sum_weight;
+
if (paint < 0.0f)
{
sum_r /= sum_weight;
sum_g /= sum_weight;
sum_b /= sum_weight;
}
- sum_a /= sum_weight;
- sum_r /= sum_a;
- sum_g /= sum_a;
- sum_b /= sum_a;
+ demul = paint < 0.0 ? sum_a : 1.0;
/* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */
- *color_r = CLAMP(sum_r, 0.0f, 1.0f);
- *color_g = CLAMP(sum_g, 0.0f, 1.0f);
- *color_b = CLAMP(sum_b, 0.0f, 1.0f);
+ *color_r = CLAMP(sum_r / demul, 0.0f, 1.0f);
+ *color_g = CLAMP(sum_g / demul, 0.0f, 1.0f);
+ *color_b = CLAMP(sum_b / demul, 0.0f, 1.0f);
*color_a = CLAMP(sum_a, 0.0f, 1.0f);
}
}
@@ -1136,4 +1239,4 @@ gimp_mypaint_surface2_new (GeglBuffer *buffer,
surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
return surface;
-}
\ No newline at end of file
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]