[gimp/wip/alxsa/mypaint-brush-v2] paint: Add support for MyPaint Brushes v2
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/alxsa/mypaint-brush-v2] paint: Add support for MyPaint Brushes v2
- Date: Sun, 16 Oct 2022 18:00:37 +0000 (UTC)
commit 72fbf5dbe9e33b7efd4591f4d80924821e6286b5
Author: Alx Sa <cmyk student gmail com>
Date: Sat Oct 1 14:52:29 2022 +0000
paint: Add support for MyPaint Brushes v2
app/core/gimpmybrush-load.c | 13 +-
app/core/gimpmybrush-private.h | 3 +
app/core/gimpmybrush.c | 35 +-
app/core/gimpmybrush.h | 3 +
app/paint/gimpmybrushcore.c | 93 ++--
app/paint/gimpmybrushoptions.c | 84 +++-
app/paint/gimpmybrushoptions.h | 5 +
app/paint/gimpmybrushsurface.c | 965 +++++++++++++++++++++++++++++++------
app/tools/gimpmybrushoptions-gui.c | 5 +
app/tools/gimpmybrushtool.c | 21 +
10 files changed, 1024 insertions(+), 203 deletions(-)
---
diff --git a/app/core/gimpmybrush-load.c b/app/core/gimpmybrush-load.c
index 1dbb9f5bd7..b9bd74b39f 100644
--- a/app/core/gimpmybrush-load.c
+++ b/app/core/gimpmybrush-load.c
@@ -84,7 +84,7 @@ gimp_mybrush_load (GimpContext *context,
return NULL;
}
- mypaint_brush = mypaint_brush_new ();
+ mypaint_brush = mypaint_brush_new_with_buckets (64);
mypaint_brush_from_defaults (mypaint_brush);
if (! mypaint_brush_from_string (mypaint_brush, (const gchar *) buffer))
@@ -147,6 +147,17 @@ gimp_mybrush_load (GimpContext *context,
mypaint_brush_get_base_value (mypaint_brush,
MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM);
+ /* Version 2 MyPaint Brush options */
+ brush->priv->pigment = -0.1;
+
+ brush->priv->posterize =
+ mypaint_brush_get_base_value (mypaint_brush,
+ MYPAINT_BRUSH_SETTING_POSTERIZE);
+
+ brush->priv->posterize_num =
+ mypaint_brush_get_base_value (mypaint_brush,
+ MYPAINT_BRUSH_SETTING_POSTERIZE_NUM);
+
mypaint_brush_unref (mypaint_brush);
return g_list_prepend (NULL, brush);
diff --git a/app/core/gimpmybrush-private.h b/app/core/gimpmybrush-private.h
index 7966c0b20d..31e0fc6be7 100644
--- a/app/core/gimpmybrush-private.h
+++ b/app/core/gimpmybrush-private.h
@@ -27,6 +27,9 @@ struct _GimpMybrushPrivate
gdouble radius;
gdouble opaque;
gdouble hardness;
+ gdouble pigment;
+ gdouble posterize;
+ gdouble posterize_num;
gdouble offset_by_random;
gboolean eraser;
};
diff --git a/app/core/gimpmybrush.c b/app/core/gimpmybrush.c
index a26c86b227..1a04bfa76c 100644
--- a/app/core/gimpmybrush.c
+++ b/app/core/gimpmybrush.c
@@ -98,10 +98,13 @@ gimp_mybrush_init (GimpMybrush *brush)
{
brush->priv = gimp_mybrush_get_instance_private (brush);
- brush->priv->radius = 1.0;
- brush->priv->opaque = 1.0;
- brush->priv->hardness = 1.0;
- brush->priv->eraser = FALSE;
+ brush->priv->radius = 1.0;
+ brush->priv->opaque = 1.0;
+ brush->priv->hardness = 1.0;
+ brush->priv->pigment = -0.1;
+ brush->priv->posterize = 0.0;
+ brush->priv->posterize_num = 1.0;
+ brush->priv->eraser = FALSE;
}
static void
@@ -264,6 +267,30 @@ gimp_mybrush_get_hardness (GimpMybrush *brush)
return brush->priv->hardness;
}
+gdouble
+gimp_mybrush_get_pigment (GimpMybrush *brush)
+{
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), -0.1);
+
+ return brush->priv->pigment;
+}
+
+gdouble
+gimp_mybrush_get_posterize (GimpMybrush *brush)
+{
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 0.0);
+
+ return brush->priv->posterize;
+}
+
+gdouble
+gimp_mybrush_get_posterize_num (GimpMybrush *brush)
+{
+ g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0);
+
+ return brush->priv->posterize_num;
+}
+
gdouble
gimp_mybrush_get_offset_by_random (GimpMybrush *brush)
{
diff --git a/app/core/gimpmybrush.h b/app/core/gimpmybrush.h
index 2dad734fe1..4bf8be8912 100644
--- a/app/core/gimpmybrush.h
+++ b/app/core/gimpmybrush.h
@@ -59,6 +59,9 @@ const gchar * gimp_mybrush_get_brush_json (GimpMybrush *brush);
gdouble gimp_mybrush_get_radius (GimpMybrush *brush);
gdouble gimp_mybrush_get_opaque (GimpMybrush *brush);
gdouble gimp_mybrush_get_hardness (GimpMybrush *brush);
+gdouble gimp_mybrush_get_pigment (GimpMybrush *brush);
+gdouble gimp_mybrush_get_posterize (GimpMybrush *brush);
+gdouble gimp_mybrush_get_posterize_num (GimpMybrush *brush);
gdouble gimp_mybrush_get_offset_by_random (GimpMybrush *brush);
gboolean gimp_mybrush_get_is_eraser (GimpMybrush *brush);
diff --git a/app/paint/gimpmybrushcore.c b/app/paint/gimpmybrushcore.c
index 108997f1ca..b7e5910cf6 100644
--- a/app/paint/gimpmybrushcore.c
+++ b/app/paint/gimpmybrushcore.c
@@ -80,7 +80,10 @@ static void gimp_mybrush_core_motion (GimpPaintCore *paint_core
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
GimpSymmetry *sym,
- guint32 time);
+ guint32 time,
+ gfloat view_zoom,
+ gfloat view_rotation,
+ gfloat barrel_rotation);
static void gimp_mybrush_core_create_brushes (GimpMybrushCore *mybrush,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
@@ -200,11 +203,12 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
GimpPaintState paint_state,
guint32 time)
{
- GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
- GimpContext *context = GIMP_CONTEXT (paint_options);
- gint offset_x;
- gint offset_y;
- GimpRGB fg;
+ GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
+ GimpMybrushOptions *mybrush_options = GIMP_MYBRUSH_OPTIONS (paint_options);
+ GimpContext *context = GIMP_CONTEXT (paint_options);
+ gint offset_x;
+ gint offset_y;
+ GimpRGB fg;
g_return_if_fail (g_list_length (drawables) == 1);
@@ -231,12 +235,14 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
case GIMP_PAINT_STATE_MOTION:
gimp_mybrush_core_motion (paint_core, drawables->data, paint_options,
- sym, time);
+ sym, time, mybrush_options->view_zoom,
+ mybrush_options->view_rotation, 1.0f);
break;
case GIMP_PAINT_STATE_FINISH:
gimp_symmetry_set_stateful (sym, FALSE);
- mypaint_surface_unref ((MyPaintSurface *) mybrush->private->surface);
+ mypaint_surface_unref (mypaint_surface2_to_surface (
+ (MyPaintSurface2 *) mybrush->private->surface));
mybrush->private->surface = NULL;
g_list_free_full (mybrush->private->brushes,
@@ -251,10 +257,14 @@ gimp_mybrush_core_motion (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
GimpSymmetry *sym,
- guint32 time)
+ guint32 time,
+ gfloat view_zoom,
+ gfloat view_rotation,
+ gfloat barrel_rotation)
{
GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
MyPaintRectangle rect;
+ MyPaintRectangles rects = {1, &rect};
GList *iter;
gdouble dt = 0.0;
gint off_x, off_y;
@@ -272,7 +282,8 @@ gimp_mybrush_core_motion (GimpPaintCore *paint_core,
gimp_mybrush_core_create_brushes (mybrush, drawable, paint_options, sym);
}
- mypaint_surface_begin_atomic ((MyPaintSurface *) mybrush->private->surface);
+ mypaint_surface_begin_atomic (mypaint_surface2_to_surface (
+ (MyPaintSurface2 *) mybrush->private->surface));
if (mybrush->private->last_time < 0)
{
@@ -284,14 +295,15 @@ gimp_mybrush_core_motion (GimpPaintCore *paint_core,
MyPaintBrush *brush = iter->data;
GimpCoords coords = *(gimp_symmetry_get_coords (sym, i));
- mypaint_brush_stroke_to (brush,
- (MyPaintSurface *) mybrush->private->surface,
- coords.x - off_x,
- coords.y - off_y,
- 0.0f,
- coords.xtilt,
- coords.ytilt,
- 1.0f /* Pretend the cursor hasn't moved in a while */);
+ mypaint_brush_stroke_to_2 (brush,
+ (MyPaintSurface2 *) mybrush->private->surface,
+ coords.x - off_x,
+ coords.y - off_y,
+ 0.0f,
+ coords.xtilt,
+ coords.ytilt,
+ 1.0f, /* Pretend the cursor hasn't moved in a while */
+ view_zoom, view_rotation, barrel_rotation);
}
dt = 0.015;
@@ -316,29 +328,30 @@ gimp_mybrush_core_motion (GimpPaintCore *paint_core,
GimpCoords coords = *(gimp_symmetry_get_coords (sym, i));
gdouble pressure = coords.pressure;
- mypaint_brush_stroke_to (brush,
- (MyPaintSurface *) mybrush->private->surface,
- coords.x - off_x,
- coords.y - off_y,
- pressure,
- coords.xtilt,
- coords.ytilt,
- dt);
+ mypaint_brush_stroke_to_2 (brush,
+ (MyPaintSurface2 *) mybrush->private->surface,
+ coords.x - off_x,
+ coords.y - off_y,
+ pressure,
+ coords.xtilt,
+ coords.ytilt,
+ dt,
+ view_zoom, view_rotation, barrel_rotation);
}
mybrush->private->last_time = time;
- mypaint_surface_end_atomic ((MyPaintSurface *) mybrush->private->surface,
- &rect);
+ mypaint_surface2_end_atomic ((MyPaintSurface2 *) mybrush->private->surface,
+ &rects);
- if (rect.width > 0 && rect.height > 0)
+ if (rects.rectangles[0].width > 0 && rects.rectangles[0].height > 0)
{
- paint_core->x1 = MIN (paint_core->x1, rect.x);
- paint_core->y1 = MIN (paint_core->y1, rect.y);
- paint_core->x2 = MAX (paint_core->x2, rect.x + rect.width);
- paint_core->y2 = MAX (paint_core->y2, rect.y + rect.height);
+ paint_core->x1 = MIN (paint_core->x1, rects.rectangles[0].x);
+ paint_core->y1 = MIN (paint_core->y1, rects.rectangles[0].y);
+ paint_core->x2 = MAX (paint_core->x2, rects.rectangles[0].x + rects.rectangles[0].width);
+ paint_core->y2 = MAX (paint_core->y2, rects.rectangles[0].y + rects.rectangles[0].height);
- gimp_drawable_update (drawable, rect.x, rect.y, rect.width, rect.height);
+ gimp_drawable_update (drawable, rects.rectangles[0].x, rects.rectangles[0].y,
rects.rectangles[0].width, rects.rectangles[0].height);
}
}
@@ -375,7 +388,7 @@ gimp_mybrush_core_create_brushes (GimpMybrushCore *mybrush,
for (i = 0; i < n_strokes; i++)
{
- MyPaintBrush *brush = mypaint_brush_new ();
+ MyPaintBrush *brush = mypaint_brush_new_with_buckets (64);
const gchar *brush_data;
mypaint_brush_from_defaults (brush);
@@ -413,6 +426,16 @@ gimp_mybrush_core_create_brushes (GimpMybrushCore *mybrush,
gimp_drawable_has_alpha (drawable)) ?
1.0f : 0.0f);
+ mypaint_brush_set_base_value (brush,
+ MYPAINT_BRUSH_SETTING_PAINT_MODE,
+ options->pigment);
+ mypaint_brush_set_base_value (brush,
+ MYPAINT_BRUSH_SETTING_POSTERIZE,
+ options->posterize);
+ mypaint_brush_set_base_value (brush,
+ MYPAINT_BRUSH_SETTING_POSTERIZE_NUM,
+ options->posterize_num);
+
mypaint_brush_new_stroke (brush);
mybrush->private->brushes = g_list_prepend (mybrush->private->brushes,
diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c
index 4d515491a8..b208b2582c 100644
--- a/app/paint/gimpmybrushoptions.c
+++ b/app/paint/gimpmybrushoptions.c
@@ -38,9 +38,14 @@
enum
{
PROP_0,
+ PROP_VIEW_ZOOM,
+ PROP_VIEW_ROTATION,
PROP_RADIUS,
PROP_OPAQUE,
PROP_HARDNESS,
+ PROP_PIGMENT,
+ PROP_POSTERIZE,
+ PROP_POSTERIZE_NUM,
PROP_ERASER,
PROP_NO_ERASING
};
@@ -82,6 +87,20 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
context_class->mybrush_changed = gimp_mybrush_options_mybrush_changed;
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
+ "viewzoom",
+ _("View Zoom"),
+ NULL,
+ 0.0001, G_MAXFLOAT, 1.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
+ "viewrotation",
+ _("View Rotation"),
+ NULL,
+ 0.0, 360.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS,
"radius",
_("Radius"),
@@ -103,6 +122,27 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
0.0, 1.0, 1.0,
GIMP_PARAM_STATIC_STRINGS);
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PIGMENT,
+ "pigment",
+ _("Pigment"),
+ _("Enable spectral blending"),
+ -0.1, 1.0, -0.1,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_POSTERIZE,
+ "posterize",
+ _("Posterize"),
+ NULL,
+ 0.0, 1.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_POSTERIZE_NUM,
+ "posterizenum",
+ _("Posterize Number"),
+ NULL,
+ 0.0, 1.28, 1.0,
+ GIMP_PARAM_STATIC_STRINGS);
+
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ERASER,
"eraser",
_("Erase with this brush"),
@@ -141,6 +181,13 @@ gimp_mybrush_options_set_property (GObject *object,
switch (property_id)
{
+ case PROP_VIEW_ZOOM:
+ options->view_zoom = g_value_get_double (value) > 0.0f ?
+ g_value_get_double (value) : 0.0001f;
+ break;
+ case PROP_VIEW_ROTATION:
+ options->view_rotation = CLAMP (g_value_get_double (value), 0.0f, 360.0f);
+ break;
case PROP_RADIUS:
options->radius = g_value_get_double (value);
break;
@@ -150,6 +197,15 @@ gimp_mybrush_options_set_property (GObject *object,
case PROP_OPAQUE:
options->opaque = g_value_get_double (value);
break;
+ case PROP_PIGMENT:
+ options->pigment = g_value_get_double (value);
+ break;
+ case PROP_POSTERIZE:
+ options->posterize = g_value_get_double (value);
+ break;
+ case PROP_POSTERIZE_NUM:
+ options->posterize_num = g_value_get_double (value);
+ break;
case PROP_ERASER:
options->eraser = g_value_get_boolean (value);
break;
@@ -173,6 +229,12 @@ gimp_mybrush_options_get_property (GObject *object,
switch (property_id)
{
+ case PROP_VIEW_ZOOM:
+ g_value_set_double (value, options->view_zoom);
+ break;
+ case PROP_VIEW_ROTATION:
+ g_value_set_double (value, options->view_rotation);
+ break;
case PROP_RADIUS:
g_value_set_double (value, options->radius);
break;
@@ -182,6 +244,15 @@ gimp_mybrush_options_get_property (GObject *object,
case PROP_HARDNESS:
g_value_set_double (value, options->hardness);
break;
+ case PROP_PIGMENT:
+ g_value_set_double (value, options->pigment);
+ break;
+ case PROP_POSTERIZE:
+ g_value_set_double (value, options->posterize);
+ break;
+ case PROP_POSTERIZE_NUM:
+ g_value_set_double (value, options->posterize_num);
+ break;
case PROP_ERASER:
g_value_set_boolean (value, options->eraser);
break;
@@ -201,10 +272,15 @@ gimp_mybrush_options_mybrush_changed (GimpContext *context,
{
if (brush)
g_object_set (context,
- "radius", gimp_mybrush_get_radius (brush),
- "opaque", gimp_mybrush_get_opaque (brush),
- "hardness", gimp_mybrush_get_hardness (brush),
- "eraser", gimp_mybrush_get_is_eraser (brush),
+ "viewzoom", 1.0f,
+ "viewrotation", 0.0f,
+ "radius", gimp_mybrush_get_radius (brush),
+ "opaque", gimp_mybrush_get_opaque (brush),
+ "hardness", gimp_mybrush_get_hardness (brush),
+ "pigment", gimp_mybrush_get_pigment (brush),
+ "posterize", gimp_mybrush_get_posterize (brush),
+ "posterizenum", gimp_mybrush_get_posterize_num (brush),
+ "eraser", gimp_mybrush_get_is_eraser (brush),
NULL);
}
diff --git a/app/paint/gimpmybrushoptions.h b/app/paint/gimpmybrushoptions.h
index b1b5cd55a3..d6305efd7a 100644
--- a/app/paint/gimpmybrushoptions.h
+++ b/app/paint/gimpmybrushoptions.h
@@ -36,9 +36,14 @@ struct _GimpMybrushOptions
{
GimpPaintOptions parent_instance;
+ gdouble view_zoom;
+ gdouble view_rotation;
gdouble radius;
gdouble opaque;
gdouble hardness;
+ gdouble pigment;
+ gdouble posterize;
+ gdouble posterize_num;
gboolean eraser;
gboolean no_erasing;
};
diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c
index 436c77ced2..1d134fa225 100644
--- a/app/paint/gimpmybrushsurface.c
+++ b/app/paint/gimpmybrushsurface.c
@@ -17,6 +17,7 @@
#include "config.h"
#include <gegl.h>
+#include <stdint.h>
#include <mypaint-surface.h>
@@ -25,25 +26,149 @@
#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"
#include "gimpmybrushoptions.h"
#include "gimpmybrushsurface.h"
+#define WGM_EPSILON 0.001
struct _GimpMybrushSurface
{
- MyPaintSurface surface;
- GeglBuffer *buffer;
- GeglBuffer *paint_mask;
- gint paint_mask_x;
- gint paint_mask_y;
- GeglRectangle dirty;
- GimpComponentMask component_mask;
+ MyPaintSurface2 surface;
+ GeglBuffer *buffer;
+ GeglBuffer *paint_mask;
+ gint paint_mask_x;
+ gint paint_mask_y;
+ GeglRectangle dirty;
+ GimpComponentMask component_mask;
GimpMybrushOptions *options;
};
+static const float T_MATRIX_SMALL[3][10] =
{{0.026595621243689,0.049779426257903,0.022449850859496,-0.218453689278271
+,-0.256894883201278,0.445881722194840,0.772365886289756,0.194498761382537
+,0.014038157587820,0.007687264480513}
+,{-0.032601672674412,-0.061021043498478,-0.052490001018404
+,0.206659098273522,0.572496335158169,0.317837248815438,-0.021216624031211
+,-0.019387668756117,-0.001521339050858,-0.000835181622534}
+,{0.339475473216284,0.635401374177222,0.771520797089589,0.113222640692379
+,-0.055251113343776,-0.048222578468680,-0.012966666339586
+,-0.001523814504223,-0.000094718948810,-0.000051604594741}};
+
+static const float spectral_r_small[10] =
{0.009281362787953,0.009732627042016,0.011254252737167,0.015105578649573
+,0.024797924177217,0.083622585502406,0.977865045723212,1.000000000000000
+,0.999961046144372,0.999999992756822};
+
+static const float spectral_g_small[10] =
{0.002854127435775,0.003917589679914,0.012132151699187,0.748259205918013
+,1.000000000000000,0.865695937531795,0.037477469241101,0.022816789725717
+,0.021747419446456,0.021384940572308};
+
+static const float spectral_b_small[10] =
{0.537052150373386,0.546646402401469,0.575501819073983,0.258778829633924
+,0.041709923751716,0.012662638828324,0.007485593127390,0.006766900622462
+,0.006699764779016,0.006676219883241};
+
+void
+rgb_to_spectral (float r,
+ float g,
+ float b,
+ float *spectral_);
+void
+spectral_to_rgb (float *spectral,
+ float *rgb_);
+
+float
+spectral_blend_factor (float x);
+
+void
+get_color_pixels_legacy (float mask,
+ float *pixel,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a);
+
+void
+get_color_pixels_accumulate (float mask,
+ float *pixel,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a,
+ float paint,
+ uint16_t sample_interval,
+ float random_sample_rate,
+ uint16_t interval_counter);
+
+void
+draw_dab_pixels_BlendMode_Normal (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_Normal_Paint
+ (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_Normal_and_Eraser
+ (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float color_a,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint
+ (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float color_a,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_LockAlpha
+ (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_LockAlpha_Paint
+ (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity);
+
+void
+draw_dab_pixels_BlendMode_Posterize
+ (float *mask,
+ float *pixel,
+ float opacity,
+ float posterize_num);
+
/* --- Taken from mypaint-tiled-surface.c --- */
static inline float
calculate_rr (int xp,
@@ -200,6 +325,401 @@ calculate_rr_antialiased (int xp,
return 1.0f - visibilityNear;
}
+
+/* -- Taken from helpers.c -- */
+void
+rgb_to_spectral (float r,
+ float g,
+ float b,
+ float *spectral_)
+{
+ float offset = 1.0 - WGM_EPSILON;
+ float spec_r[10] = {0};
+ float spec_g[10] = {0};
+ float spec_b[10] = {0};
+
+ r = r * offset + WGM_EPSILON;
+ g = g * offset + WGM_EPSILON;
+ b = b * offset + WGM_EPSILON;
+ /* upsample rgb to spectral primaries */
+
+ for (int i = 0; i < 10; i++) {
+ spec_r[i] = spectral_r_small[i] * r;
+ }
+ for (int i = 0; i < 10; i++) {
+ spec_g[i] = spectral_g_small[i] * g;
+ }
+ for (int i = 0; i < 10; i++) {
+ spec_b[i] = spectral_b_small[i] * b;
+ }
+ //collapse into one spd
+ for (int i = 0; i < 10; i++) {
+ spectral_[i] += spec_r[i] + spec_g[i] + spec_b[i];
+ }
+}
+
+void
+spectral_to_rgb (float *spectral,
+ float *rgb_)
+{
+ float offset = 1.0 - WGM_EPSILON;
+ /* We need this tmp. array to allow auto vectorization. */
+ float tmp[3] = {0};
+ for (int i=0; i<10; i++) {
+ tmp[0] += T_MATRIX_SMALL[0][i] * spectral[i];
+ tmp[1] += T_MATRIX_SMALL[1][i] * spectral[i];
+ tmp[2] += T_MATRIX_SMALL[2][i] * spectral[i];
+ }
+ for (int i=0; i<3; i++) {
+ rgb_[i] = CLAMP((tmp[i] - WGM_EPSILON) / offset, 0.0f, 1.0f);
+ }
+}
+
+/* -- Taken from brushmode.c -- */
+void
+get_color_pixels_legacy (float mask,
+ float *pixel,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a)
+{
+ *sum_r += mask * pixel[RED];
+ *sum_g += mask * pixel[GREEN];
+ *sum_b += mask * pixel[BLUE];
+ *sum_a += mask * pixel[ALPHA];
+ *sum_weight += mask;
+}
+
+void
+get_color_pixels_accumulate (float mask,
+ float *pixel,
+ float *sum_weight,
+ float *sum_r,
+ float *sum_g,
+ float *sum_b,
+ float *sum_a,
+ float paint,
+ uint16_t sample_interval,
+ float random_sample_rate,
+ uint16_t interval_counter)
+{
+ 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};
+
+ /* V1 Brush Code */
+ if (paint < 0.0f)
+ {
+ get_color_pixels_legacy (mask, pixel, sum_weight,
+ sum_r, sum_g, sum_b, sum_a);
+ return;
+ }
+
+ rgb_to_spectral (*sum_r, *sum_g, *sum_b, avg_spectral);
+
+ if (interval_counter == 0 || rand() < random_sample_threshold)
+ {
+ float fac_a, fac_b;
+ float a = mask * pixel[ALPHA];
+ float alpha_sums = a + *sum_a;
+
+ *sum_weight += mask;
+
+ fac_a = fac_b = 1.0f;
+ if (alpha_sums > 0.0f)
+ {
+ fac_a = a / alpha_sums;
+ fac_b = 1.0 - fac_a;
+ }
+
+ if (paint > 0.0f && pixel[ALPHA] > 0)
+ {
+ float spectral[10] = {0};
+ rgb_to_spectral (pixel[RED] / pixel[ALPHA],
+ pixel[GREEN] / pixel[ALPHA],
+ pixel[BLUE] / pixel[ALPHA],
+ spectral);
+
+ for (int i = 0; i < 10; i++)
+ avg_spectral[i] =
+ fastpow (spectral[i], fac_a) * fastpow (avg_spectral[i], fac_b);
+ }
+
+ if (paint < 1.0f && pixel[ALPHA] > 0)
+ {
+ for (int i = 0; i < 3; i++)
+ avg_rgb[i] = pixel[i] * fac_a / pixel[ALPHA] + avg_rgb[i] * fac_b;
+ }
+
+ *sum_a += a;
+ }
+
+ spectral_to_rgb (avg_spectral, spec_rgb);
+
+ *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);
+}
+
+void
+draw_dab_pixels_BlendMode_Normal (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity)
+{
+ float src_term = *mask * opacity;
+ float dst_term = 1.0f - src_term;
+
+ pixel[RED] = color_r * src_term + pixel[RED] * dst_term;
+ pixel[GREEN] = color_g * src_term + pixel[GREEN] * dst_term;
+ pixel[BLUE] = color_b * src_term + pixel[BLUE] * dst_term;
+}
+
+void
+draw_dab_pixels_BlendMode_Normal_Paint (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity)
+{
+ float spectral_a[10] = {0};
+ float spectral_b[10] = {0};
+ float spectral_result[10] = {0};
+ float rgb_result[3] = {0};
+ float src_term;
+ float dst_term;
+ float fac_a;
+ float fac_b;
+
+ rgb_to_spectral (color_r, color_g, color_b, spectral_a);
+ opacity = MAX (opacity, 1.5);
+
+ src_term = *mask * opacity;
+ dst_term = 1.0f - src_term;
+
+ if (pixel[ALPHA] <= 0)
+ {
+ pixel[ALPHA] = src_term + dst_term * pixel[ALPHA];
+ pixel[RED] = src_term * color_r + dst_term * pixel[RED];
+ pixel[GREEN] = src_term * color_g + dst_term * pixel[GREEN];
+ pixel[BLUE] = src_term * color_b + dst_term * pixel[BLUE];
+ }
+ else
+ {
+ fac_a = src_term / (src_term + dst_term * pixel[ALPHA]);
+ fac_b = 1.0f - fac_a;
+
+ rgb_to_spectral (pixel[RED] / pixel[ALPHA],
+ pixel[GREEN] / pixel[ALPHA],
+ pixel[BLUE] / pixel[ALPHA],
+ spectral_b);
+
+ for (int i = 0; i < 10; i++)
+ spectral_result[i] =
+ fastpow (spectral_a[i], fac_a) * fastpow (spectral_b[i], fac_b);
+
+ spectral_to_rgb (spectral_result, rgb_result);
+ pixel[ALPHA] = src_term + dst_term * pixel[ALPHA];
+
+ pixel[RED] = (rgb_result[0] * pixel[ALPHA]);
+ pixel[GREEN] = (rgb_result[1] * pixel[ALPHA]);
+ pixel[BLUE] = (rgb_result[2] * pixel[ALPHA]);
+ }
+}
+
+void
+draw_dab_pixels_BlendMode_Normal_and_Eraser (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float color_a,
+ float opacity)
+{
+ float src_term = *mask * opacity;
+ float dst_term = 1.0f - src_term;
+
+ src_term *= color_a;
+
+ pixel[RED] = color_r * src_term + pixel[RED] * dst_term;
+ pixel[GREEN] = color_g * src_term + pixel[GREEN] * dst_term;
+ pixel[BLUE] = color_b * src_term + pixel[BLUE] * dst_term;
+}
+
+void
+draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float color_a,
+ float opacity)
+{
+ 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 = *mask * opacity;
+ 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;
+ float fac_b;
+ float spectral_factor;
+ float 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 * pixel[RED];
+ rgb[1] = src_term2 * color_g + dst_term * pixel[GREEN];
+ rgb[2] = src_term2 * color_b + dst_term * pixel[BLUE];
+ }
+
+ if (spectral_factor && pixel[ALPHA] != 0)
+ {
+ /* Convert straightened tile pixel color to a spectral */
+ rgb_to_spectral (pixel[RED] / pixel[ALPHA],
+ pixel[GREEN] / pixel[ALPHA],
+ pixel[BLUE] / 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;
+ pixel[RED] = rgb[0];
+ pixel[GREEN] = rgb[1];
+ pixel[BLUE] = rgb[2];
+}
+
+void
+draw_dab_pixels_BlendMode_LockAlpha (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity)
+{
+ float src_term = *mask * opacity;
+ float dst_term = 1.0f - src_term;
+
+ src_term /= pixel[ALPHA];
+
+ pixel[RED] = color_r * src_term + pixel[RED] * dst_term;
+ pixel[GREEN] = color_g * src_term + pixel[GREEN] * dst_term;
+ pixel[BLUE] = color_b * src_term + pixel[BLUE] * dst_term;
+}
+
+void draw_dab_pixels_BlendMode_LockAlpha_Paint (float *mask,
+ float *pixel,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opacity)
+{
+ float spectral_a[10] = {0};
+ float spectral_b[10] = {0};
+ float spectral_result[10] = {0};
+ float rgb_result[3] = {0};
+ float src_term;
+ float dst_term;
+ float fac_a;
+ float fac_b;
+
+ rgb_to_spectral (color_r, color_g, color_b, spectral_a);
+ opacity = MAX (opacity, 1.5);
+
+ src_term = *mask * opacity;
+ dst_term = 1.0f - src_term;
+ src_term *= pixel[ALPHA];
+
+ if (pixel[ALPHA] <= 0)
+ {
+ pixel[RED] = src_term * color_r + dst_term * pixel[RED];
+ pixel[GREEN] = src_term * color_g + dst_term * pixel[GREEN];
+ pixel[BLUE] = src_term * color_b + dst_term * pixel[BLUE];
+ }
+ else
+ {
+ fac_a = src_term / (src_term + dst_term * pixel[ALPHA]);
+ fac_b = 1.0f - fac_a;
+
+ rgb_to_spectral (pixel[RED] / pixel[ALPHA],
+ pixel[GREEN] / pixel[ALPHA],
+ pixel[BLUE] / pixel[ALPHA],
+ spectral_b);
+
+ for (int i = 0; i < 10; i++)
+ spectral_result[i] =
+ fastpow (spectral_a[i], fac_a) * fastpow (spectral_b[i], fac_b);
+
+ spectral_to_rgb (spectral_result, rgb_result);
+ pixel[ALPHA] = src_term + dst_term * pixel[ALPHA];
+
+ pixel[RED] = rgb_result[0] * pixel[ALPHA];
+ pixel[GREEN] = rgb_result[1] * pixel[ALPHA];
+ pixel[BLUE] = rgb_result[2] * pixel[ALPHA];
+ }
+}
+
+void
+draw_dab_pixels_BlendMode_Posterize (float *mask,
+ float *pixel,
+ float opacity,
+ float posterize_num)
+{
+ float post_r = ROUND (pixel[RED] * posterize_num) / posterize_num;
+ float post_g = ROUND (pixel[GREEN] * posterize_num) / posterize_num;
+ float post_b = ROUND (pixel[BLUE] * posterize_num) / posterize_num;
+
+ float src_term = *mask * opacity;
+ float dst_term = 1 - src_term;
+
+ pixel[RED] = src_term * post_r + dst_term * pixel[RED];
+ pixel[GREEN] = src_term * post_g + dst_term * pixel[GREEN];
+ pixel[BLUE] = src_term * post_b + dst_term * pixel[BLUE];
+}
+
+
/* -- end mypaint code */
static inline float
@@ -230,128 +750,43 @@ calculate_dab_roi (float x,
}
static void
-gimp_mypaint_surface_get_color (MyPaintSurface *base_surface,
- float x,
- float y,
- float radius,
- float *color_r,
- float *color_g,
- float *color_b,
- float *color_a)
+gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface)
{
- GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
- GeglRectangle dabRect;
-
- if (radius < 1.0f)
- radius = 1.0f;
-
- dabRect = calculate_dab_roi (x, y, radius);
-
- *color_r = 0.0f;
- *color_g = 0.0f;
- *color_b = 0.0f;
- *color_a = 0.0f;
-
- if (dabRect.width > 0 || dabRect.height > 0)
- {
- const float one_over_radius2 = 1.0f / (radius * radius);
- float sum_weight = 0.0f;
- float sum_r = 0.0f;
- float sum_g = 0.0f;
- float sum_b = 0.0f;
- float sum_a = 0.0f;
- /* Read in clamp mode to avoid transparency bleeding in at the edges */
- GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
- babl_format ("R'aG'aB'aA float"),
- GEGL_BUFFER_READ,
- GEGL_ABYSS_CLAMP, 2);
- if (surface->paint_mask)
- {
- GeglRectangle mask_roi = dabRect;
- mask_roi.x -= surface->paint_mask_x;
- mask_roi.y -= surface->paint_mask_y;
- gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
- babl_format ("Y float"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- }
-
- while (gegl_buffer_iterator_next (iter))
- {
- float *pixel = (float *)iter->items[0].data;
- float *mask;
- int iy, ix;
-
- if (surface->paint_mask)
- mask = iter->items[1].data;
- else
- mask = NULL;
-
- 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);
- for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++)
- {
- /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */
- float xx = (ix + 0.5f - x);
- float rr = (yy * yy + xx * xx) * one_over_radius2;
- float pixel_weight = 0.0f;
- if (rr <= 1.0f)
- pixel_weight = 1.0f - rr;
- if (mask)
- pixel_weight *= *mask;
-
- sum_r += pixel_weight * pixel[RED];
- sum_g += pixel_weight * pixel[GREEN];
- sum_b += pixel_weight * pixel[BLUE];
- sum_a += pixel_weight * pixel[ALPHA];
- sum_weight += pixel_weight;
-
- pixel += 4;
- if (mask)
- mask += 1;
- }
- }
- }
-
- if (sum_a > 0.0f && sum_weight > 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;
+}
- /* 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_a = CLAMP(sum_a, 0.0f, 1.0f);
- }
- }
+static void
+gimp_mypaint_surface_destroy (MyPaintSurface *base_surface)
+{
+ GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
+ g_clear_object (&surface->buffer);
+ g_clear_object (&surface->paint_mask);
+ g_free (surface);
}
+/* MyPaintSurface2 implementation */
static int
-gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
- float x,
- float y,
- float radius,
- float color_r,
- float color_g,
- float color_b,
- float opaque,
- float hardness,
- float color_a,
- float aspect_ratio,
- float angle,
- float lock_alpha,
- float colorize)
+gimp_mypaint_surface_draw_dab_2 (MyPaintSurface2 *base_surface,
+ float x,
+ float y,
+ float radius,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opaque,
+ float hardness,
+ float color_a,
+ float aspect_ratio,
+ float angle,
+ float lock_alpha,
+ float colorize,
+ float posterize,
+ float posterize_num,
+ float paint)
{
- GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
+ /* Placeholder - eventually implement here */
+ GimpMybrushSurface *surface = (GimpMybrushSurface *) base_surface;
GeglBufferIterator *iter;
GeglRectangle dabRect;
GimpComponentMask component_mask = surface->component_mask;
@@ -374,7 +809,8 @@ gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
r_aa_start = MAX (r_aa_start, 0);
r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio;
- normal_mode = opaque * (1.0f - colorize);
+ posterize = CLAMP (posterize, 0.0f, 1.0f);
+ normal_mode = opaque * (1.0f - colorize) * (1.0f - posterize);
colorize = opaque * colorize;
/* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
@@ -432,16 +868,52 @@ gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
g = pixel[GREEN];
b = pixel[BLUE];
- if (a > 0.0f)
+ /* v1 Brush code */
+ if (paint < 1.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 (color_a == 1.0f)
+ draw_dab_pixels_BlendMode_Normal (&alpha, pixel,
+ color_r, color_g, color_b,
+ normal_mode * opaque * (1 - paint));
+ else
+ draw_dab_pixels_BlendMode_Normal_and_Eraser (&alpha, pixel,
+ color_r, color_g,
+ color_b, color_a,
+ normal_mode * opaque * (1 - paint));
+
+ if (lock_alpha > 0.0f && color_a != 0)
+ draw_dab_pixels_BlendMode_LockAlpha (&alpha, pixel,
+ color_r, color_g, color_b,
+ lock_alpha * opaque * (1 - colorize) *
+ (1 - posterize) * (1 - paint));
+
+ r = pixel[RED];
+ g = pixel[GREEN];
+ b = pixel[BLUE];
+ }
+
+ /* v2 Brush code */
+ if (paint > 0.0f)
+ {
+ if (color_a == 1.0f)
+ draw_dab_pixels_BlendMode_Normal_Paint (&alpha, pixel,
+ color_r, color_g, color_b,
+ normal_mode * opaque * paint);
+ else
+ draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint (&alpha, pixel,
+ color_r, color_g,
+ color_b, color_a,
+ normal_mode * opaque * paint);
+
+ if (lock_alpha > 0.0f && color_a != 0)
+ draw_dab_pixels_BlendMode_LockAlpha_Paint (&alpha, pixel,
+ color_r, color_g, color_b,
+ lock_alpha * opaque * (1 - colorize) *
+ (1 - posterize) * paint);
+
+ r = pixel[RED];
+ g = pixel[GREEN];
+ b = pixel[BLUE];
}
if (colorize > 0.0f && base_alpha > 0.0f)
@@ -469,6 +941,11 @@ gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
}
}
+ if (posterize > 0.0f)
+ draw_dab_pixels_BlendMode_Posterize (&alpha, pixel,
+ opaque, posterize_num);
+
+
if (surface->options->no_erasing)
a = MAX (a, pixel[ALPHA]);
@@ -501,33 +978,195 @@ gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
return 1;
}
+static int
+gimp_mypaint_surface_draw_dab_wrapper (MyPaintSurface *surface,
+ float x,
+ float y,
+ float radius,
+ float color_r,
+ float color_g,
+ float color_b,
+ float opaque,
+ float hardness,
+ float color_a,
+ float aspect_ratio,
+ float angle,
+ float lock_alpha,
+ float colorize)
+{
+ const gfloat posterize = 0.0;
+ const gfloat posterize_num = 1.0;
+ const gfloat pigment = 0.0;
+
+ return gimp_mypaint_surface_draw_dab_2 ((MyPaintSurface2 *) surface, x, y, radius,
+ color_r, color_g, color_b, opaque, hardness,
+ color_a, aspect_ratio, angle, lock_alpha,
+ colorize, posterize, posterize_num,
+ pigment);
+}
+
static void
-gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface)
+gimp_mypaint_surface_get_color_2 (MyPaintSurface2 *base_surface,
+ float x,
+ float y,
+ float radius,
+ float *color_r,
+ float *color_g,
+ float *color_b,
+ float *color_a,
+ float paint)
{
+ GimpMybrushSurface *surface = (GimpMybrushSurface *) base_surface;
+ GeglRectangle dabRect;
+ if (radius < 1.0f)
+ radius = 1.0f;
+
+ dabRect = calculate_dab_roi (x, y, radius);
+
+ *color_r = 0.0f;
+ *color_g = 0.0f;
+ *color_b = 0.0f;
+ *color_a = 0.0f;
+
+ if (dabRect.width > 0 || dabRect.height > 0)
+ {
+ const float one_over_radius2 = 1.0f / (radius * radius);
+ const int sample_interval = radius <= 2.0f ? 1 : (int)(radius * 7);
+ const float random_sample_rate = 1.0f / (7 * radius);
+ float sum_weight = 0.0f;
+ float sum_r = 0.0f;
+ float sum_g = 0.0f;
+ float sum_b = 0.0f;
+ float sum_a = 0.0f;
+
+ /* Read in clamp mode to avoid transparency bleeding in at the edges */
+ GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
+ babl_format ("R'aG'aB'aA float"),
+ GEGL_BUFFER_READ,
+ GEGL_ABYSS_CLAMP, 2);
+ if (surface->paint_mask)
+ {
+ GeglRectangle mask_roi = dabRect;
+ mask_roi.x -= surface->paint_mask_x;
+ mask_roi.y -= surface->paint_mask_y;
+ gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
+ babl_format ("Y float"),
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ }
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ 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);
+ for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++)
+ {
+ /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */
+ float xx = (ix + 0.5f - x);
+ float rr = (yy * yy + xx * xx) * one_over_radius2;
+ float pixel_weight = 0.0f;
+ if (rr <= 1.0f)
+ pixel_weight = 1.0f - rr;
+ if (mask)
+ pixel_weight *= *mask;
+
+ #ifdef _OPENMP
+ #pragma omp critical
+ #endif
+ get_color_pixels_accumulate (pixel_weight, pixel, &sum_weight,
+ &sum_r, &sum_g, &sum_b, &sum_a, paint,
+ sample_interval, random_sample_rate,
+ interval_counter);
+
+ interval_counter = (interval_counter + 1) % sample_interval;
+
+ pixel += 4;
+ if (mask)
+ mask += 1;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ 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 / 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);
+ }
+ }
}
static void
-gimp_mypaint_surface_end_atomic (MyPaintSurface *base_surface,
- MyPaintRectangle *roi)
+gimp_mypaint_surface_get_color_wrapper (MyPaintSurface *surface,
+ float x,
+ float y,
+ float radius,
+ float *color_r,
+ float *color_g,
+ float *color_b,
+ float *color_a)
{
- GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
-
- roi->x = surface->dirty.x;
- roi->y = surface->dirty.y;
- roi->width = surface->dirty.width;
- roi->height = surface->dirty.height;
- surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
+ return gimp_mypaint_surface_get_color_2 ((MyPaintSurface2 *) surface, x, y, radius,
+ color_r, color_g, color_b, color_a,
+ -1.0);
}
+
static void
-gimp_mypaint_surface_destroy (MyPaintSurface *base_surface)
+gimp_mypaint_surface_end_atomic_2 (MyPaintSurface2 *base_surface,
+ MyPaintRectangles *roi)
{
- GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
+ GimpMybrushSurface *surface = (GimpMybrushSurface *) base_surface;
- g_clear_object (&surface->buffer);
- g_clear_object (&surface->paint_mask);
- g_free (surface);
+ if (roi)
+ {
+ const gint roi_rects = roi->num_rectangles;
+
+ for (gint i = 0; i < roi_rects; i++)
+ {
+ roi->rectangles[i].x = surface->dirty.x;
+ roi->rectangles[i].y = surface->dirty.y;
+ roi->rectangles[i].width = surface->dirty.width;
+ roi->rectangles[i].height = surface->dirty.height;
+ surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
+ }
+ }
+}
+
+static void
+gimp_mypaint_surface_end_atomic_wrapper (MyPaintSurface *surface,
+ MyPaintRectangle *roi)
+{
+ MyPaintRectangles rois = {1, roi};
+ gimp_mypaint_surface_end_atomic_2 ((MyPaintSurface2 *) surface, &rois);
}
GimpMybrushSurface *
@@ -539,23 +1178,31 @@ gimp_mypaint_surface_new (GeglBuffer *buffer,
GimpMybrushOptions *options)
{
GimpMybrushSurface *surface = g_malloc0 (sizeof (GimpMybrushSurface));
+ MyPaintSurface2 *s;
+
+ mypaint_surface_init (&surface->surface.parent);
+ s = &surface->surface;
+
+ s->get_color_pigment = gimp_mypaint_surface_get_color_2;
+ s->draw_dab_pigment = gimp_mypaint_surface_draw_dab_2;
+ s->parent.begin_atomic = gimp_mypaint_surface_begin_atomic;
+ s->end_atomic_multi = gimp_mypaint_surface_end_atomic_2;
+
+ s->parent.draw_dab = gimp_mypaint_surface_draw_dab_wrapper;
+ s->parent.get_color = gimp_mypaint_surface_get_color_wrapper;
+ s->parent.end_atomic = gimp_mypaint_surface_end_atomic_wrapper;
- mypaint_surface_init ((MyPaintSurface *)surface);
+ s->parent.destroy = gimp_mypaint_surface_destroy;
- surface->surface.get_color = gimp_mypaint_surface_get_color;
- surface->surface.draw_dab = gimp_mypaint_surface_draw_dab;
- surface->surface.begin_atomic = gimp_mypaint_surface_begin_atomic;
- surface->surface.end_atomic = gimp_mypaint_surface_end_atomic;
- surface->surface.destroy = gimp_mypaint_surface_destroy;
- surface->component_mask = component_mask;
- surface->options = options;
- surface->buffer = g_object_ref (buffer);
+ surface->component_mask = component_mask;
+ surface->options = options;
+ surface->buffer = g_object_ref (buffer);
if (paint_mask)
- surface->paint_mask = g_object_ref (paint_mask);
+ surface->paint_mask = g_object_ref (paint_mask);
- surface->paint_mask_x = paint_mask_x;
- surface->paint_mask_y = paint_mask_y;
- surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
+ surface->paint_mask_x = paint_mask_x;
+ surface->paint_mask_y = paint_mask_y;
+ surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
return surface;
}
diff --git a/app/tools/gimpmybrushoptions-gui.c b/app/tools/gimpmybrushoptions-gui.c
index a2f927f920..0bfc418e3c 100644
--- a/app/tools/gimpmybrushoptions-gui.c
+++ b/app/tools/gimpmybrushoptions-gui.c
@@ -78,5 +78,10 @@ gimp_mybrush_options_gui (GimpToolOptions *tool_options)
0.1, 1.0, 2);
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ /* pigment */
+ scale = gimp_prop_spin_scale_new (config, "pigment",
+ 0.1, 0.0, 2);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+
return vbox;
}
diff --git a/app/tools/gimpmybrushtool.c b/app/tools/gimpmybrushtool.c
index 2a7378802f..d8964b67d7 100644
--- a/app/tools/gimpmybrushtool.c
+++ b/app/tools/gimpmybrushtool.c
@@ -56,6 +56,10 @@ static GimpCanvasItem * gimp_mybrush_tool_get_outline (GimpPaintTool *paint_tool
gdouble x,
gdouble y);
+static void gimp_mybrush_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display);
void
gimp_mybrush_tool_register (GimpToolRegisterCallback callback,
@@ -84,6 +88,7 @@ gimp_mybrush_tool_class_init (GimpMybrushToolClass *klass)
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
GimpPaintToolClass *paint_tool_class = GIMP_PAINT_TOOL_CLASS (klass);
+ tool_class->cursor_update = gimp_mybrush_tool_cursor_update;
tool_class->options_notify = gimp_mybrush_tool_options_notify;
paint_tool_class->get_outline = gimp_mybrush_tool_get_outline;
@@ -176,3 +181,19 @@ gimp_mybrush_tool_create_cursor (GimpPaintTool *paint_tool,
return NULL;
}
+
+static void gimp_mybrush_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpDisplayShell *shell;
+ GimpMybrushOptions *options = GIMP_MYBRUSH_TOOL_GET_OPTIONS (tool);
+
+ g_return_if_fail (GIMP_IS_DISPLAY (display));
+
+ shell = gimp_display_get_shell (display);
+
+ options->view_zoom = gimp_zoom_model_get_factor (shell->zoom);
+ options->view_rotation = shell->rotate_angle;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]