[gimp] Bug 785001 - Enhanced smudge tool: Smudge with painting
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Bug 785001 - Enhanced smudge tool: Smudge with painting
- Date: Sun, 23 Jul 2017 12:44:45 +0000 (UTC)
commit bcda53ee7b7725a078cc9033cfb3717659da360e
Author: Michael Natterer <mitch gimp org>
Date: Sat Jul 22 18:20:23 2017 +0200
Bug 785001 - Enhanced smudge tool: Smudge with painting
Apply patches from shark0r that alow to mix in both constant and
gradient color while smudging; and clean up the patches.
app/gegl/gimp-gegl-loops.c | 159 +++++++++++++++++++++++++++++---------
app/gegl/gimp-gegl-loops.h | 16 ++--
app/paint/gimpsmudge.c | 141 +++++++++++++++++++++++----------
app/paint/gimpsmudgeoptions.c | 44 +++++++++--
app/paint/gimpsmudgeoptions.h | 2 +
app/tools/gimppaintoptions-gui.c | 3 +-
app/tools/gimpsmudgetool.c | 13 +++-
7 files changed, 284 insertions(+), 94 deletions(-)
---
diff --git a/app/gegl/gimp-gegl-loops.c b/app/gegl/gimp-gegl-loops.c
index 1824cdc..21508c8 100644
--- a/app/gegl/gimp-gegl-loops.c
+++ b/app/gegl/gimp-gegl-loops.c
@@ -20,6 +20,12 @@
#include "config.h"
+#include <string.h>
+
+#if COMPILE_SSE2_INTRINISICS
+#include <emmintrin.h>
+#endif
+
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
@@ -347,69 +353,148 @@ gimp_gegl_dodgeburn (GeglBuffer *src_buffer,
}
}
-/*
- * blend_pixels patched 8-24-05 to fix bug #163721. Note that this change
- * causes the function to treat src1 and src2 asymmetrically. This gives the
- * right behavior for the smudge tool, which is the only user of this function
- * at the time of patching. If you want to use the function for something
- * else, caveat emptor.
+/* helper function of gimp_gegl_smudge_with_paint()
+ src and dest can be the same address
+ */
+static 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)
+{
+
+/* 2017/4/13 shark0r : According to my test, SSE decreases about 25%
+ * execution time
+ */
+
+#if defined COMPILE_SSE2_INTRINISICS
+
+ __m128 v_src1 = _mm_loadu_ps (src1);
+ __m128 v_src2 = _mm_loadu_ps (src2);
+ __m128 *v_dest = (__v4sf *) dest;
+
+ gfloat orginal_src2_alpha = v_src2[3];
+ gfloat src1_alpha = src1_rate * v_src1[3];
+ gfloat src2_alpha = src2_rate * orginal_src2_alpha;
+ gfloat result_alpha = src1_alpha + src2_alpha;
+
+ if (result_alpha == 0)
+ {
+ *v_dest = _mm_set1_ps (0);
+ return;
+ }
+
+ *v_dest = (v_src1 * _mm_set1_ps (src1_alpha) +
+ v_src2 * _mm_set1_ps (src2_alpha)) /
+ _mm_set1_ps (result_alpha);
+
+#else
+
+ gfloat orginal_src2_alpha = src2[3];
+ gfloat src1_alpha = src1_rate * src1[3];
+ gfloat src2_alpha = src2_rate * orginal_src2_alpha;
+ gfloat result_alpha = src1_alpha + src2_alpha;
+ gint b;
+
+ 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;
+
+#endif
+
+ if (no_erasing_src2)
+ {
+ result_alpha = MAX (result_alpha, orginal_src2_alpha);
+ }
+
+ dest[3] = result_alpha;
+}
+
+/* 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_blend (GeglBuffer *top_buffer,
- const GeglRectangle *top_rect,
- GeglBuffer *bottom_buffer,
- const GeglRectangle *bottom_rect,
- GeglBuffer *dest_buffer,
- const GeglRectangle *dest_rect,
- gdouble blend)
+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)
{
GeglBufferIterator *iter;
+ gfloat brush_color_float[4];
+ gfloat brush_a = flow;
+ GeglAccessMode paint_buffer_access_mode = (brush_color ?
+ GEGL_ACCESS_WRITE :
+ GEGL_ACCESS_READWRITE);
- iter = gegl_buffer_iterator_new (top_buffer, top_rect, 0,
+ iter = gegl_buffer_iterator_new (accum_buffer, accum_rect, 0,
babl_format ("RGBA float"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
- gegl_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0,
+ gegl_buffer_iterator_add (iter, canvas_buffer, canvas_rect, 0,
babl_format ("RGBA float"),
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- gegl_buffer_iterator_add (iter, dest_buffer, dest_rect, 0,
+ gegl_buffer_iterator_add (iter, paint_buffer, GEGL_RECTANGLE (0, 0, 0, 0), 0,
babl_format ("RGBA float"),
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+ paint_buffer_access_mode, GEGL_ABYSS_NONE);
+
+ /* 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];
+ }
while (gegl_buffer_iterator_next (iter))
{
- const gfloat *top = iter->data[0];
- const gfloat *bottom = iter->data[1];
- gfloat *dest = iter->data[2];
+ gfloat *accum = iter->data[0];
+ const gfloat *canvas = iter->data[1];
+ gfloat *paint = iter->data[2];
gint count = iter->length;
- const gfloat blend1 = 1.0 - blend;
- const gfloat blend2 = blend;
while (count--)
{
- const gfloat a1 = blend1 * bottom[3];
- const gfloat a2 = blend2 * top[3];
- const gfloat a = a1 + a2;
- gint b;
+ /* blend accum_buffer and canvas_buffer to accum_buffer */
+ gimp_gegl_smudge_with_paint_blend (accum, rate, canvas, 1 - rate,
+ accum, no_erasing);
- if (a == 0)
+ /* blend accum_buffer and brush color/pixmap to paint_buffer */
+ if (brush_a == 0) /* pure smudge */
{
- for (b = 0; b < 4; b++)
- dest[b] = 0;
+ memcpy (paint, accum, sizeof (gfloat) * 4);
}
else
{
- for (b = 0; b < 3; b++)
- dest[b] =
- bottom[b] + (bottom[b] * a1 + top[b] * a2 - a * bottom[b]) / a;
+ gfloat *src1 = brush_color ? brush_color_float : paint;
- dest[3] = a;
+ gimp_gegl_smudge_with_paint_blend (src1, flow, accum, 1 - flow,
+ paint, no_erasing);
}
- top += 4;
- bottom += 4;
- dest += 4;
+ accum += 4;
+ canvas += 4;
+ paint += 4;
}
}
}
diff --git a/app/gegl/gimp-gegl-loops.h b/app/gegl/gimp-gegl-loops.h
index e7dcc68..9528051 100644
--- a/app/gegl/gimp-gegl-loops.h
+++ b/app/gegl/gimp-gegl-loops.h
@@ -43,13 +43,15 @@ void gimp_gegl_dodgeburn (GeglBuffer *src_buffer,
GimpDodgeBurnType type,
GimpTransferMode mode);
-void gimp_gegl_smudge_blend (GeglBuffer *top_buffer,
- const GeglRectangle *top_rect,
- GeglBuffer *bottom_buffer,
- const GeglRectangle *bottom_rect,
- GeglBuffer *dest_buffer,
- const GeglRectangle *dest_rect,
- gdouble blend);
+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);
void gimp_gegl_apply_mask (GeglBuffer *mask_buffer,
const GeglRectangle *mask_rect,
diff --git a/app/paint/gimpsmudge.c b/app/paint/gimpsmudge.c
index 5a1d8d4..6c1d9eb 100644
--- a/app/paint/gimpsmudge.c
+++ b/app/paint/gimpsmudge.c
@@ -277,23 +277,31 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
GimpPaintOptions *paint_options,
GimpSymmetry *sym)
{
- GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
- GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
- GimpContext *context = GIMP_CONTEXT (paint_options);
- GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
- GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+ GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
+ GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
+ GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
+ GimpContext *context = GIMP_CONTEXT (paint_options);
+ GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
GeglBuffer *paint_buffer;
gint paint_buffer_x;
gint paint_buffer_y;
gint paint_buffer_width;
gint paint_buffer_height;
+ /* brush dynamics */
gdouble fade_point;
gdouble opacity;
gdouble rate;
- gdouble dynamic_rate;
+ gdouble flow;
+ gdouble grad_point;
+ /* brush color */
+ GimpRGB brush_color;
+ GimpRGB *brush_color_ptr; /* whether use single color or pixmap */
+ /* accum buffer */
gint x, y;
- gdouble force;
GeglBuffer *accum_buffer;
+ /* other variables */
+ gdouble force;
GimpCoords *coords;
GeglNode *op;
gint paint_width, paint_height;
@@ -317,6 +325,52 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
paint_options,
coords);
+ /* Get brush dynamic values other than opacity */
+ rate = ((options->rate / 100.0) *
+ gimp_dynamics_get_linear_value (dynamics,
+ GIMP_DYNAMICS_OUTPUT_RATE,
+ coords,
+ paint_options,
+ fade_point));
+
+ flow = ((options->flow / 100.0) *
+ gimp_dynamics_get_linear_value (dynamics,
+ GIMP_DYNAMICS_OUTPUT_FLOW,
+ coords,
+ paint_options,
+ fade_point));
+
+ grad_point = gimp_dynamics_get_linear_value (dynamics,
+ GIMP_DYNAMICS_OUTPUT_COLOR,
+ coords,
+ paint_options,
+ fade_point);
+
+ /* Get current gradient color, brush pixmap, or foreground color */
+ brush_color_ptr = &brush_color;
+ if (gimp_paint_options_get_gradient_color (paint_options, image,
+ grad_point,
+ paint_core->pixel_dist,
+ &brush_color))
+ {
+ /* No more processing needed */
+ }
+ else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush))
+ {
+ brush_color_ptr = NULL;
+ }
+ else
+ {
+ gimp_context_get_foreground (context, &brush_color);
+ }
+
+ /* Convert to linear RGBA */
+ if (brush_color_ptr)
+ gimp_pickable_srgb_to_pixel (GIMP_PICKABLE (drawable),
+ &brush_color,
+ babl_format ("RGBA double"),
+ &brush_color);
+
n_strokes = gimp_symmetry_get_size (sym);
for (i = 0; i < n_strokes; i++)
{
@@ -343,16 +397,10 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
/* Get the unclipped acumulator coordinates */
gimp_smudge_accumulator_coords (paint_core, coords, i, &x, &y);
- /* Enable dynamic rate */
- dynamic_rate = gimp_dynamics_get_linear_value (dynamics,
- GIMP_DYNAMICS_OUTPUT_RATE,
- coords,
- paint_options,
- fade_point);
-
- rate = (options->rate / 100.0) * dynamic_rate;
+ accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
- /* Smudge uses the buffer Accum.
+ /* Old smudge tool:
+ * Smudge uses the buffer Accum.
* For each successive painthit Accum is built like this
* Accum = rate*Accum + (1-rate)*I.
* where I is the pixels under the current painthit.
@@ -360,33 +408,42 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
* (Accum,1) (if no alpha),
*/
- accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
+ /* 2017/4/22: New smudge painting tool:
+ * Accum=rate*Accum + (1-rate)*I
+ * if brush_color_ptr!=NULL
+ * Paint=(1-flow)*Accum + flow*BrushColor
+ * else, draw brush pixmap on the paint_buffer and
+ * Paint=(1-flow)*Accum + flow*Paint
+ *
+ * For non-pixmap brushes, calculate blending in
+ * gimp_gegl_smudge_with_paint() instead of calling
+ * gegl_buffer_set_color() to reduce gegl's internal processing.
+ */
+ if (! brush_color_ptr)
+ {
+ gimp_brush_core_color_area_with_pixmap (brush_core, drawable,
+ coords, op,
+ paint_buffer,
+ paint_buffer_x,
+ paint_buffer_y,
+ gimp_paint_options_get_brush_mode (paint_options));
+ }
- gimp_gegl_smudge_blend (accum_buffer,
- GEGL_RECTANGLE (paint_buffer_x - x,
- paint_buffer_y - y,
- paint_buffer_width,
- paint_buffer_height),
- gimp_drawable_get_buffer (drawable),
- GEGL_RECTANGLE (paint_buffer_x,
- paint_buffer_y,
- paint_buffer_width,
- paint_buffer_height),
- accum_buffer,
- GEGL_RECTANGLE (paint_buffer_x - x,
- paint_buffer_y - y,
- paint_buffer_width,
- paint_buffer_height),
- rate);
-
- gegl_buffer_copy (accum_buffer,
- GEGL_RECTANGLE (paint_buffer_x - x,
- paint_buffer_y - y,
- paint_buffer_width,
- paint_buffer_height),
- GEGL_ABYSS_NONE,
- paint_buffer,
- GEGL_RECTANGLE (0, 0, 0, 0));
+ gimp_gegl_smudge_with_paint (accum_buffer,
+ GEGL_RECTANGLE (paint_buffer_x - x,
+ paint_buffer_y - y,
+ paint_buffer_width,
+ paint_buffer_height),
+ gimp_drawable_get_buffer (drawable),
+ GEGL_RECTANGLE (paint_buffer_x,
+ paint_buffer_y,
+ paint_buffer_width,
+ paint_buffer_height),
+ brush_color_ptr,
+ paint_buffer,
+ options->no_erasing,
+ flow,
+ rate);
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
force = gimp_dynamics_get_linear_value (dynamics,
diff --git a/app/paint/gimpsmudgeoptions.c b/app/paint/gimpsmudgeoptions.c
index 73c98f0..1c0096b 100644
--- a/app/paint/gimpsmudgeoptions.c
+++ b/app/paint/gimpsmudgeoptions.c
@@ -29,13 +29,17 @@
#include "gimp-intl.h"
-#define SMUDGE_DEFAULT_RATE 50.0
+#define SMUDGE_DEFAULT_RATE 50.0
+#define SMUDGE_DEFAULT_FLOW 0.0
+#define SMUDGE_DEFAULT_NO_ERASING FALSE
enum
{
PROP_0,
- PROP_RATE
+ PROP_RATE,
+ PROP_FLOW,
+ PROP_NO_ERASING,
};
@@ -64,9 +68,23 @@ gimp_smudge_options_class_init (GimpSmudgeOptionsClass *klass)
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RATE,
"rate",
C_("smudge-tool", "Rate"),
- NULL,
+ _("The strength of smudging"),
0.0, 100.0, SMUDGE_DEFAULT_RATE,
GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FLOW,
+ "flow",
+ C_("smudge-tool", "Flow"),
+ _("The amount of brush color to blend"),
+ 0.0, 100.0, SMUDGE_DEFAULT_FLOW,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_NO_ERASING,
+ "no-erasing",
+ C_("smudge-tool", "No erasing effect"),
+ _("Never decrease alpha of existing pixels"),
+ SMUDGE_DEFAULT_NO_ERASING,
+ GIMP_PARAM_STATIC_STRINGS);
}
static void
@@ -87,6 +105,13 @@ gimp_smudge_options_set_property (GObject *object,
case PROP_RATE:
options->rate = g_value_get_double (value);
break;
+ case PROP_FLOW:
+ options->flow = g_value_get_double (value);
+ break;
+ case PROP_NO_ERASING:
+ options->no_erasing = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -95,9 +120,9 @@ gimp_smudge_options_set_property (GObject *object,
static void
gimp_smudge_options_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
{
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (object);
@@ -106,6 +131,13 @@ gimp_smudge_options_get_property (GObject *object,
case PROP_RATE:
g_value_set_double (value, options->rate);
break;
+ case PROP_FLOW:
+ g_value_set_double (value, options->flow);
+ break;
+ case PROP_NO_ERASING:
+ g_value_set_boolean (value, options->no_erasing);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
diff --git a/app/paint/gimpsmudgeoptions.h b/app/paint/gimpsmudgeoptions.h
index 3c1a07a..291feae 100644
--- a/app/paint/gimpsmudgeoptions.h
+++ b/app/paint/gimpsmudgeoptions.h
@@ -37,6 +37,8 @@ struct _GimpSmudgeOptions
GimpPaintOptions parent_instance;
gdouble rate;
+ gdouble flow;
+ gboolean no_erasing;
};
struct _GimpSmudgeOptionsClass
diff --git a/app/tools/gimppaintoptions-gui.c b/app/tools/gimppaintoptions-gui.c
index 0573b94..42f1852 100644
--- a/app/tools/gimppaintoptions-gui.c
+++ b/app/tools/gimppaintoptions-gui.c
@@ -364,7 +364,8 @@ dynamics_options_gui (GimpPaintOptions *paint_options,
gtk_widget_show (checkbox);
/* Color UI */
- if (g_type_is_a (tool_type, GIMP_TYPE_PAINTBRUSH_TOOL))
+ if (g_type_is_a (tool_type, GIMP_TYPE_PAINTBRUSH_TOOL) ||
+ tool_type == GIMP_TYPE_SMUDGE_TOOL)
{
inner_frame = gimp_frame_new (_("Color Options"));
gtk_box_pack_start (GTK_BOX (vbox), inner_frame, FALSE, FALSE, 0);
diff --git a/app/tools/gimpsmudgetool.c b/app/tools/gimpsmudgetool.c
index ee5be8a..c332d08 100644
--- a/app/tools/gimpsmudgetool.c
+++ b/app/tools/gimpsmudgetool.c
@@ -49,7 +49,8 @@ gimp_smudge_tool_register (GimpToolRegisterCallback callback,
(* callback) (GIMP_TYPE_SMUDGE_TOOL,
GIMP_TYPE_SMUDGE_OPTIONS,
gimp_smudge_options_gui,
- GIMP_PAINT_OPTIONS_CONTEXT_MASK,
+ GIMP_PAINT_OPTIONS_CONTEXT_MASK |
+ GIMP_CONTEXT_PROP_MASK_GRADIENT,
"gimp-smudge-tool",
_("Smudge"),
_("Smudge Tool: Smudge selectively using a brush"),
@@ -86,6 +87,11 @@ gimp_smudge_options_gui (GimpToolOptions *tool_options)
GObject *config = G_OBJECT (tool_options);
GtkWidget *vbox = gimp_paint_options_gui (tool_options);
GtkWidget *scale;
+ GtkWidget *button;
+
+ button = gimp_prop_check_button_new (config, "no-erasing", NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
/* the rate scale */
scale = gimp_prop_spin_scale_new (config, "rate", NULL,
@@ -93,5 +99,10 @@ gimp_smudge_options_gui (GimpToolOptions *tool_options)
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
+ scale = gimp_prop_spin_scale_new (config, "flow", NULL,
+ 1.0, 10.0, 1);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
return vbox;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]