[gimp/wip/pippin: 70/71] Enhanced smudge tool: Add painting ability to it. Detailed here: https://mail.gnome.org/archives/gim
- From: Øyvind Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/pippin: 70/71] Enhanced smudge tool: Add painting ability to it. Detailed here: https://mail.gnome.org/archives/gim
- Date: Sat, 13 May 2017 19:34:55 +0000 (UTC)
commit 81a7a1348b582e8cdf975e1b577047b6e5585d53
Author: shark0r <b91502038 ntu edu tw>
Date: Fri Apr 14 19:25:41 2017 +0800
Enhanced smudge tool: Add painting ability to it. Detailed here:
https://mail.gnome.org/archives/gimp-developer-list/2017-April/msg00006.html
app/gegl/gimp-gegl-loops.c | 134 ++++++++++++++++++++++++++++-------------
app/gegl/gimp-gegl-loops.h | 16 +++--
app/paint/gimpsmudge.c | 93 ++++++++++++++++------------
app/paint/gimpsmudgeoptions.c | 35 ++++++++++-
app/paint/gimpsmudgeoptions.h | 6 +-
app/tools/gimpsmudgetool.c | 10 +++-
6 files changed, 199 insertions(+), 95 deletions(-)
---
diff --git a/app/gegl/gimp-gegl-loops.c b/app/gegl/gimp-gegl-loops.c
index 1824cdc..843d744 100644
--- a/app/gegl/gimp-gegl-loops.c
+++ b/app/gegl/gimp-gegl-loops.c
@@ -23,6 +23,10 @@
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
+#include <string.h>
+#if COMPILE_SSE2_INTRINISICS
+#include <emmintrin.h>
+#endif
#include "libgimpmath/gimpmath.h"
#include "libgimpcolor/gimpcolor.h"
@@ -347,69 +351,117 @@ 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() */
+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
+
+ __v4sf v_src1 = _mm_load_ps(src1);
+ __v4sf v_src2 = _mm_load_ps(src2);
+ __v4sf *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;
+ if (result_alpha == 0)
+ {
+ memset(dest, 0, sizeof(gfloat)*4);
+ return;
+ }
+
+ gint b;
+ 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;
+}
+
+/* Customized smudge tool. Currently only smudge tool uses this function
+ * Accum = rate*Accum + (1-rate)*Canvas
+ * Paint = flow*brushColor + (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_effect,
+ gdouble flow,
+ gdouble rate)
{
GeglBufferIterator *iter;
-
- 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_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+ 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);
+ gint b;
+ //convert to float
+ const gdouble *brush_color_ptr = &brush_color->r;
+ gfloat brush_color_float[4];
+ for( b=0;b<4;b++ )
+ brush_color_float[b] = brush_color_ptr[b];
+ const gfloat brush_a = flow * 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_effect);
- if (a == 0)
+ //blend accum_buffer and brush_color 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;
-
- dest[3] = a;
+ gimp_gegl_smudge_with_paint_blend(brush_color_float, flow, accum, 1-flow, paint,
no_erasing_effect);
}
-
- 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..0cc7fdf 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_effect,
+ 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 e068ff1..c1ba611 100644
--- a/app/paint/gimpsmudge.c
+++ b/app/paint/gimpsmudge.c
@@ -94,12 +94,8 @@ gimp_smudge_class_init (GimpSmudgeClass *klass)
GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass);
object_class->finalize = gimp_smudge_finalize;
-
paint_core_class->paint = gimp_smudge_paint;
-
brush_core_class->handles_changing_brush = TRUE;
- brush_core_class->handles_transforming_brush = TRUE;
- brush_core_class->handles_dynamic_transforming_brush = TRUE;
}
static void
@@ -285,6 +281,8 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
gdouble opacity;
gdouble rate;
gdouble dynamic_rate;
+ gdouble flow;
+ gdouble dynamic_flow;
gint x, y;
gdouble force;
GeglBuffer *accum_buffer;
@@ -311,6 +309,34 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
paint_options,
coords);
+ /* 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;
+
+ /* Get flow value */
+ dynamic_flow = gimp_dynamics_get_linear_value (dynamics,
+ GIMP_DYNAMICS_OUTPUT_FLOW,
+ coords,
+ paint_options,
+ fade_point);
+ flow = (options->flow / 100.0) * dynamic_flow;
+
+ /* Get current foreground color
+ * TODO: support color from gradient and color brush
+ */
+ GimpRGB foreground;
+ gimp_context_get_foreground (context, &foreground);
+ gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), &foreground, &foreground);
+ //convert from gamma to linear RGBA
+ //may call babl function directly, but not sure whether internal of gimp_gegl_color_new() will change
+ GeglColor *gegl_color = gimp_gegl_color_new (&foreground);
+ gegl_color_get_pixel(gegl_color, babl_format ("RGBA double"), &foreground);
+ g_object_unref (gegl_color);
+
n_strokes = gimp_symmetry_get_size (sym);
for (i = 0; i < n_strokes; i++)
{
@@ -336,17 +362,10 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
/* Get the unclipped acumulator coordinates */
gimp_smudge_accumulator_coords (paint_core, coords, i, &x, &y);
+ accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
- /* 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;
-
- /* Smudge uses the buffer Accum.
+ /* Origional 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.
@@ -354,32 +373,26 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
* (Accum,1) (if no alpha),
*/
- accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
- 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));
+ /* Customized smudge tool:
+ * Accum=rate*Accum + (1-rate)*I
+ * Paint=(1-flow)*Accum + flow*BrushColor
+ */
+
+ 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),
+ &foreground,
+ paint_buffer,
+ options->no_erasing_effect,
+ 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..f5216ce 100644
--- a/app/paint/gimpsmudgeoptions.c
+++ b/app/paint/gimpsmudgeoptions.c
@@ -30,12 +30,15 @@
#define SMUDGE_DEFAULT_RATE 50.0
-
+#define SMUDGE_DEFAULT_FLOW 0.0
+#define SMUDGE_DEFAULT_NO_ERASING_EFFECT FALSE
enum
{
PROP_0,
- PROP_RATE
+ PROP_RATE,
+ PROP_FLOW,
+ PROP_NO_ERASING_EFFECT,
};
@@ -61,12 +64,24 @@ gimp_smudge_options_class_init (GimpSmudgeOptionsClass *klass)
object_class->set_property = gimp_smudge_options_set_property;
object_class->get_property = gimp_smudge_options_get_property;
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_NO_ERASING_EFFECT,
+ "no-erasing-effect",
+ C_("smudge-tool", "No erasing effect"),
+ _("Never decrease alpha of existing pixels"),
+ SMUDGE_DEFAULT_NO_ERASING_EFFECT,
+ GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RATE,
"rate",
- C_("smudge-tool", "Rate"),
- NULL,
+ _("Rate"),
+ _("The strength of smudging"),
0.0, 100.0, SMUDGE_DEFAULT_RATE,
GIMP_PARAM_STATIC_STRINGS);
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FLOW,
+ "flow",
+ _("Flow"),
+ _("The amount of brush color to blend"),
+ 0.0, 100.0, SMUDGE_DEFAULT_FLOW,
+ GIMP_PARAM_STATIC_STRINGS);
}
static void
@@ -84,9 +99,15 @@ gimp_smudge_options_set_property (GObject *object,
switch (property_id)
{
+ case PROP_NO_ERASING_EFFECT:
+ options->no_erasing_effect = g_value_get_boolean(value);
+ break;
case PROP_RATE:
options->rate = g_value_get_double (value);
break;
+ case PROP_FLOW:
+ options->flow = g_value_get_double (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -103,9 +124,15 @@ gimp_smudge_options_get_property (GObject *object,
switch (property_id)
{
+ case PROP_NO_ERASING_EFFECT:
+ g_value_set_boolean (value, options->no_erasing_effect);
+ break;
case PROP_RATE:
g_value_set_double (value, options->rate);
break;
+ case PROP_FLOW:
+ g_value_set_double (value, options->flow);
+ 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..f4aa03e 100644
--- a/app/paint/gimpsmudgeoptions.h
+++ b/app/paint/gimpsmudgeoptions.h
@@ -34,9 +34,11 @@ typedef struct _GimpSmudgeOptionsClass GimpSmudgeOptionsClass;
struct _GimpSmudgeOptions
{
- GimpPaintOptions parent_instance;
+ GimpPaintOptions parent_instance;
- gdouble rate;
+ gdouble rate;
+ gdouble flow;
+ gboolean no_erasing_effect;
};
struct _GimpSmudgeOptionsClass
diff --git a/app/tools/gimpsmudgetool.c b/app/tools/gimpsmudgetool.c
index ee5be8a..566e269 100644
--- a/app/tools/gimpsmudgetool.c
+++ b/app/tools/gimpsmudgetool.c
@@ -85,7 +85,10 @@ gimp_smudge_options_gui (GimpToolOptions *tool_options)
{
GObject *config = G_OBJECT (tool_options);
GtkWidget *vbox = gimp_paint_options_gui (tool_options);
- GtkWidget *scale;
+ GtkWidget *scale, *button;
+ button = gimp_prop_check_button_new (config, "no-erasing-effect", 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 +96,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]