[gimp] app: Fix MyPaint brush rendering, implement MyPaintSurface in a native format



commit df21e0785568b7bfe0d6558fa588e941070a4ed3
Author: Daniel Sabo <DanielSabo gmail com>
Date:   Fri Dec 11 07:39:28 2015 -0800

    app: Fix MyPaint brush rendering, implement MyPaintSurface in a native format

 app/paint/Makefile.am              |    2 +
 app/paint/gimpmybrush.c            |  124 ++++++----
 app/paint/gimpmybrushoptions.c     |   82 +++++++
 app/paint/gimpmybrushoptions.h     |    3 +
 app/paint/gimpmybrushsurface.c     |  453 ++++++++++++++++++++++++++++++++++++
 app/paint/gimpmybrushsurface.h     |   35 +++
 app/tools/gimpmybrushoptions-gui.c |    7 +
 7 files changed, 654 insertions(+), 52 deletions(-)
---
diff --git a/app/paint/Makefile.am b/app/paint/Makefile.am
index 22e4b25..06c64bb 100644
--- a/app/paint/Makefile.am
+++ b/app/paint/Makefile.am
@@ -56,6 +56,8 @@ libapppaint_a_sources = \
        gimpmybrush.h                   \
        gimpmybrushoptions.c            \
        gimpmybrushoptions.h            \
+       gimpmybrushsurface.c            \
+       gimpmybrushsurface.h            \
        gimppaintcore.c                 \
        gimppaintcore.h                 \
        gimppaintcore-loops.c           \
diff --git a/app/paint/gimpmybrush.c b/app/paint/gimpmybrush.c
index 69aa5d9..2e47ca2 100644
--- a/app/paint/gimpmybrush.c
+++ b/app/paint/gimpmybrush.c
@@ -26,8 +26,10 @@
 #include <gegl.h>
 
 #include <mypaint-brush.h>
+#if 0
 #include <mypaint-tiled-surface.h>
 #include <mypaint-gegl-surface.h>
+#endif
 
 #include "libgimpmath/gimpmath.h"
 #include "libgimpcolor/gimpcolor.h"
@@ -45,6 +47,7 @@
 #include "core/gimpimage-undo.h"
 #include "core/gimptempbuf.h"
 
+#include "gimpmybrushsurface.h"
 #include "gimpmybrushoptions.h"
 #include "gimpmybrush.h"
 
@@ -53,8 +56,13 @@
 
 struct _GimpMybrushPrivate
 {
+#if 0
   MyPaintGeglTiledSurface *surface;
+#else
+  GimpMybrushSurface      *surface;
+#endif
   MyPaintBrush            *brush;
+  gint64                   lastTime;
 };
 
 
@@ -119,7 +127,13 @@ gimp_mybrush_paint (GimpPaintCore    *paint_core,
 {
   GimpMybrush        *mybrush = GIMP_MYBRUSH (paint_core);
   GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options);
+  const gchar        *brush_data;
+#if 0
   GeglBuffer         *buffer;
+  GimpComponentMask   active_mask;
+#endif
+  GimpRGB             fg;
+  GimpHSV             hsv;
 
   switch (paint_state)
     {
@@ -132,6 +146,7 @@ gimp_mybrush_paint (GimpPaintCore    *paint_core,
           gimp_palettes_add_color_history (context->gimp,
                                            &foreground);
 
+#if 0
           mybrush->private->surface = mypaint_gegl_tiled_surface_new ();
 
           buffer = mypaint_gegl_tiled_surface_get_buffer (mybrush->private->surface);
@@ -144,26 +159,51 @@ gimp_mybrush_paint (GimpPaintCore    *paint_core,
                             buffer, NULL);
           mypaint_gegl_tiled_surface_set_buffer (mybrush->private->surface, buffer);
           g_object_unref (buffer);
+#else
+          mybrush->private->surface = gimp_mypaint_surface_new (gimp_drawable_get_buffer (drawable),
+                                                                gimp_drawable_get_active_mask (drawable));
+#endif
 
           mybrush->private->brush = mypaint_brush_new ();
           mypaint_brush_from_defaults (mybrush->private->brush);
+          brush_data = gimp_mybrush_options_get_brush_data (options);
+          if (brush_data)
+            mypaint_brush_from_string (mybrush->private->brush, brush_data);
 
-          if (options->mybrush)
-            {
-              gchar *string;
-              gsize  length;
+#if 0
+          active_mask = gimp_drawable_get_active_mask (drawable);
 
-              if (g_file_get_contents (options->mybrush,
-                                       &string, &length, NULL))
-                {
-                  if (! mypaint_brush_from_string (mybrush->private->brush, string))
-                    g_printerr ("Failed to deserialize MyPaint brush\n");
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_LOCK_ALPHA,
+                                        (active_mask & GIMP_COMPONENT_MASK_ALPHA) ?
+                                        FALSE : TRUE);
+#endif
 
-                  g_free (string);
-                }
-            }
+          gimp_context_get_foreground (context, &fg);
+          gimp_rgb_to_hsv (&fg, &hsv);
+
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_COLOR_H,
+                                        hsv.h);
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_COLOR_S,
+                                        hsv.s);
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_COLOR_V,
+                                        hsv.v);
+
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
+                                        options->radius);
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_OPAQUE,
+                                        options->opaque * gimp_context_get_opacity (context));
+          mypaint_brush_set_base_value (mybrush->private->brush,
+                                        MYPAINT_BRUSH_SETTING_HARDNESS,
+                                        options->hardness);
 
           mypaint_brush_new_stroke (mybrush->private->brush);
+          mybrush->private->lastTime = 0;
         }
       break;
 
@@ -189,45 +229,26 @@ gimp_mybrush_motion (GimpPaintCore    *paint_core,
                      guint32           time)
 {
   GimpMybrush        *mybrush = GIMP_MYBRUSH (paint_core);
-  GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options);
-  GimpContext        *context = GIMP_CONTEXT (paint_options);
-  GimpComponentMask   active_mask;
-  GimpRGB             fg;
-  GimpHSV             hsv;
   MyPaintRectangle    rect;
 
-  active_mask = gimp_drawable_get_active_mask (drawable);
-
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_LOCK_ALPHA,
-                                (active_mask & GIMP_COMPONENT_MASK_ALPHA) ?
-                                FALSE : TRUE);
-
-  gimp_context_get_foreground (context, &fg);
-  gimp_rgb_to_hsv (&fg, &hsv);
-
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_COLOR_H,
-                                hsv.h);
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_COLOR_S,
-                                hsv.s);
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_COLOR_V,
-                                hsv.v);
-
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_OPAQUE,
-                                gimp_context_get_opacity (context));
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
-                                options->radius);
-  mypaint_brush_set_base_value (mybrush->private->brush,
-                                MYPAINT_BRUSH_SETTING_HARDNESS,
-                                options->hardness);
-
   mypaint_surface_begin_atomic ((MyPaintSurface *) mybrush->private->surface);
 
+
+  if (mybrush->private->lastTime == 0)
+    {
+      /* First motion, so we need a zero pressure event to start the stroke */
+      mybrush->private->lastTime = (gint64)time - 15;
+
+      mypaint_brush_stroke_to (mybrush->private->brush,
+                               (MyPaintSurface *) mybrush->private->surface,
+                               coords->x,
+                               coords->y,
+                               0.0f,
+                               coords->xtilt,
+                               coords->ytilt,
+                               1.0f /* Pretend the cursor hasn't moved in a while */);
+    }
+
   mypaint_brush_stroke_to (mybrush->private->brush,
                            (MyPaintSurface *) mybrush->private->surface,
                            coords->x,
@@ -235,16 +256,15 @@ gimp_mybrush_motion (GimpPaintCore    *paint_core,
                            coords->pressure,
                            coords->xtilt,
                            coords->ytilt,
-                           1);
+                           (time - mybrush->private->lastTime) * 0.001f);
+  mybrush->private->lastTime = time;
 
   mypaint_surface_end_atomic ((MyPaintSurface *) mybrush->private->surface,
                               &rect);
 
-  g_printerr ("painted rect: %d %d %d %d\n",
-              rect.x, rect.y, rect.width, rect.height);
-
   if (rect.width > 0 && rect.height > 0)
     {
+#if 0
       GeglBuffer *src;
 
       src = mypaint_gegl_tiled_surface_get_buffer (mybrush->private->surface);
@@ -254,7 +274,7 @@ gimp_mybrush_motion (GimpPaintCore    *paint_core,
                         GEGL_ABYSS_NONE,
                         gimp_drawable_get_buffer (drawable),
                         NULL);
-
+#endif
       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);
diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c
index 1e05ddd..a089b08 100644
--- a/app/paint/gimpmybrushoptions.c
+++ b/app/paint/gimpmybrushoptions.c
@@ -34,15 +34,26 @@
 
 #include "gimp-intl.h"
 
+#include <mypaint-brush.h>
 
 enum
 {
   PROP_0,
   PROP_RADIUS,
+  PROP_OPAQUE,
   PROP_HARDNESS,
   PROP_MYBRUSH
 };
 
+typedef struct
+{
+  gdouble           radius;
+  gdouble           opaque;
+  gdouble           hardness;
+  gchar            *brush_json;
+} OptionsState;
+
+static GHashTable *loaded_myb;
 
 static void   gimp_mybrush_options_set_property (GObject      *object,
                                                  guint         property_id,
@@ -52,6 +63,12 @@ static void   gimp_mybrush_options_get_property (GObject      *object,
                                                  guint         property_id,
                                                  GValue       *value,
                                                  GParamSpec   *pspec);
+static void   options_state_free (gpointer options)
+{
+  OptionsState *state = options;
+  g_free (state->brush_json);
+  g_free (state);
+}
 
 
 G_DEFINE_TYPE (GimpMybrushOptions, gimp_mybrush_options, GIMP_TYPE_PAINT_OPTIONS)
@@ -69,6 +86,10 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
                                    "radius", _("Radius"),
                                    -2.0, 6.0, 1.0,
                                    GIMP_PARAM_STATIC_STRINGS);
+  GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OPAQUE,
+                                   "opaque", _("Base Opacity"),
+                                   0.0, 2.0, 1.0,
+                                   GIMP_PARAM_STATIC_STRINGS);
   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_HARDNESS,
                                    "hardness", NULL,
                                    0.0, 1.0, 1.0,
@@ -77,6 +98,50 @@ gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass)
                                    "mybrush", NULL,
                                    NULL,
                                    GIMP_PARAM_STATIC_STRINGS);
+
+  loaded_myb = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, options_state_free);
+}
+
+static void
+gimp_mybrush_options_load_path (GObject    *object,
+                                gchar      *path)
+{
+  GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (object);
+
+  OptionsState *state = g_hash_table_lookup (loaded_myb, path);
+  if (!state)
+    {
+      gchar        *brush_json = NULL;
+      MyPaintBrush *brush = mypaint_brush_new ();
+
+      state = g_new0 (OptionsState, 1);
+      mypaint_brush_from_defaults (brush);
+
+      if (g_file_get_contents (path, &brush_json, NULL, NULL))
+        {
+          if (! mypaint_brush_from_string (brush, brush_json))
+            {
+              g_printerr ("Failed to deserialize MyPaint brush\n");
+              g_free (brush_json);
+              brush_json = NULL;
+            }
+        }
+
+      state->brush_json = brush_json;
+      state->radius = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC);
+      state->opaque = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_OPAQUE);
+      state->hardness = mypaint_brush_get_base_value (brush, MYPAINT_BRUSH_SETTING_HARDNESS);
+
+      g_hash_table_insert (loaded_myb, g_strdup(path), state);
+    }
+
+  options->radius = state->radius;
+  options->opaque = state->opaque;
+  options->hardness = state->hardness;
+
+  g_object_notify (object, "radius");
+  g_object_notify (object, "opaque");
+  g_object_notify (object, "hardness");
 }
 
 static void
@@ -100,9 +165,14 @@ gimp_mybrush_options_set_property (GObject      *object,
     case PROP_HARDNESS:
       options->hardness = g_value_get_double (value);
       break;
+    case PROP_OPAQUE:
+      options->opaque = g_value_get_double (value);
+      break;
     case PROP_MYBRUSH:
       g_free (options->mybrush);
       options->mybrush = g_value_dup_string (value);
+      if (options->mybrush)
+        gimp_mybrush_options_load_path (object, options->mybrush);
       break;
 
     default:
@@ -124,6 +194,9 @@ gimp_mybrush_options_get_property (GObject    *object,
     case PROP_RADIUS:
       g_value_set_double (value, options->radius);
       break;
+    case PROP_OPAQUE:
+      g_value_set_double (value, options->opaque);
+      break;
     case PROP_HARDNESS:
       g_value_set_double (value, options->hardness);
       break;
@@ -137,4 +210,13 @@ gimp_mybrush_options_get_property (GObject    *object,
     }
 }
 
+const gchar *
+gimp_mybrush_options_get_brush_data (GimpMybrushOptions *options)
+{
+  OptionsState *state = g_hash_table_lookup (loaded_myb, options->mybrush);
+  if (state)
+    return state->brush_json;
+  return NULL;
+}
+
 #endif
diff --git a/app/paint/gimpmybrushoptions.h b/app/paint/gimpmybrushoptions.h
index 0d069cc..a7bea73 100644
--- a/app/paint/gimpmybrushoptions.h
+++ b/app/paint/gimpmybrushoptions.h
@@ -39,6 +39,7 @@ struct _GimpMybrushOptions
   GimpPaintOptions  parent_instance;
 
   gdouble           radius;
+  gdouble           opaque;
   gdouble           hardness;
   gchar            *mybrush;
 };
@@ -51,6 +52,8 @@ struct _GimpMybrushOptionsClass
 
 GType   gimp_mybrush_options_get_type (void) G_GNUC_CONST;
 
+const gchar *
+gimp_mybrush_options_get_brush_data (GimpMybrushOptions *options);
 
 #endif  /*  __GIMP_MYBRUSH_OPTIONS_H__  */
 
diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c
new file mode 100644
index 0000000..26a4290
--- /dev/null
+++ b/app/paint/gimpmybrushsurface.c
@@ -0,0 +1,453 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#ifdef HAVE_LIBMYPAINT
+
+#include <gegl.h>
+#include "gimpmybrushsurface.h"
+#include <mypaint-surface.h>
+#include <math.h>
+
+struct _GimpMybrushSurface
+{
+  MyPaintSurface surface;
+  GeglBuffer *buffer;
+  GeglRectangle dirty;
+  GimpComponentMask component_mask;
+};
+
+/* --- Taken from mypaint-tiled-surface.c --- */
+static inline float
+calculate_rr (int   xp,
+              int   yp,
+              float x,
+              float y,
+              float aspect_ratio,
+              float sn,
+              float cs,
+              float one_over_radius2)
+{
+    /* code duplication, see brush::count_dabs_to() */
+    const float yy = (yp + 0.5f - y);
+    const float xx = (xp + 0.5f - x);
+    const float yyr=(yy*cs-xx*sn)*aspect_ratio;
+    const float xxr=yy*sn+xx*cs;
+    const float rr = (yyr*yyr + xxr*xxr) * one_over_radius2;
+    /* rr is in range 0.0..1.0*sqrt(2) */
+    return rr;
+}
+
+static inline float
+calculate_r_sample (float x,
+                    float y,
+                    float aspect_ratio,
+                    float sn,
+                    float cs)
+{
+    const float yyr=(y*cs-x*sn)*aspect_ratio;
+    const float xxr=y*sn+x*cs;
+    const float r = (yyr*yyr + xxr*xxr);
+    return r;
+}
+
+static inline float
+sign_point_in_line (float px,
+                    float py,
+                    float vx,
+                    float vy)
+{
+    return (px - vx) * (-vy) - (vx) * (py - vy);
+}
+
+static inline void
+closest_point_to_line (float  lx,
+                       float  ly,
+                       float  px,
+                       float  py,
+                       float *ox,
+                       float *oy)
+{
+    const float l2 = lx*lx + ly*ly;
+    const float ltp_dot = px*lx + py*ly;
+    const float t = ltp_dot / l2;
+    *ox = lx * t;
+    *oy = ly * t;
+}
+
+
+/* This works by taking the visibility at the nearest point
+ * and dividing by 1.0 + delta.
+ *
+ * - nearest point: point where the dab has more influence
+ * - farthest point: point at a fixed distance away from
+ *                   the nearest point
+ * - delta: how much occluded is the farthest point relative
+ *          to the nearest point
+ */
+static inline float
+calculate_rr_antialiased (int  xp,
+                          int  yp,
+                          float x,
+                          float y,
+                          float aspect_ratio,
+                          float sn,
+                          float cs,
+                          float one_over_radius2,
+                          float r_aa_start)
+{
+    /* calculate pixel position and borders in a way
+     * that the dab's center is always at zero */
+    float pixel_right = x - (float)xp;
+    float pixel_bottom = y - (float)yp;
+    float pixel_center_x = pixel_right - 0.5f;
+    float pixel_center_y = pixel_bottom - 0.5f;
+    float pixel_left = pixel_right - 1.0f;
+    float pixel_top = pixel_bottom - 1.0f;
+
+    float nearest_x, nearest_y; /* nearest to origin, but still inside pixel */
+    float farthest_x, farthest_y; /* farthest from origin, but still inside pixel */
+    float r_near, r_far, rr_near, rr_far;
+    float center_sign, rad_area_1, visibilityNear, delta, delta2;
+
+    /* Dab's center is inside pixel? */
+    if( pixel_left<0 && pixel_right>0 &&
+        pixel_top<0 && pixel_bottom>0 )
+    {
+        nearest_x = 0;
+        nearest_y = 0;
+        r_near = rr_near = 0;
+    }
+    else
+    {
+        closest_point_to_line( cs, sn, pixel_center_x, pixel_center_y, &nearest_x, &nearest_y );
+        nearest_x = CLAMP( nearest_x, pixel_left, pixel_right );
+        nearest_y = CLAMP( nearest_y, pixel_top, pixel_bottom );
+        /* XXX: precision of "nearest" values could be improved
+         * by intersecting the line that goes from nearest_x/Y to 0
+         * with the pixel's borders here, however the improvements
+         * would probably not justify the perdormance cost.
+         */
+        r_near = calculate_r_sample( nearest_x, nearest_y, aspect_ratio, sn, cs );
+        rr_near = r_near * one_over_radius2;
+    }
+
+    /* out of dab's reach? */
+    if( rr_near > 1.0f )
+        return rr_near;
+
+    /* check on which side of the dab's line is the pixel center */
+    center_sign = sign_point_in_line( pixel_center_x, pixel_center_y, cs, -sn );
+
+    /* radius of a circle with area=1
+     *   A = pi * r * r
+     *   r = sqrt(1/pi)
+     */
+    rad_area_1 = sqrtf( 1.0f / M_PI );
+
+    /* center is below dab */
+    if( center_sign < 0 )
+    {
+        farthest_x = nearest_x - sn*rad_area_1;
+        farthest_y = nearest_y + cs*rad_area_1;
+    }
+    /* above dab */
+    else
+    {
+        farthest_x = nearest_x + sn*rad_area_1;
+        farthest_y = nearest_y - cs*rad_area_1;
+    }
+
+    r_far = calculate_r_sample( farthest_x, farthest_y, aspect_ratio, sn, cs );
+    rr_far = r_far * one_over_radius2;
+
+    /* check if we can skip heavier AA */
+    if( r_far < r_aa_start )
+        return (rr_far+rr_near) * 0.5f;
+
+    /* calculate AA approximate */
+    visibilityNear = 1.0f - rr_near;
+    delta = rr_far - rr_near;
+    delta2 = 1.0f + delta;
+    visibilityNear /= delta2;
+
+    return 1.0f - visibilityNear;
+}
+/* -- end mypaint code */
+
+static inline float
+calculate_alpha_for_rr (float rr,
+                        float hardness,
+                        float slope1,
+                        float slope2)
+{
+  if (rr > 1.0f)
+    return 0.0f;
+  else if (rr <= hardness)
+    return 1.0f + rr * slope1;
+  else
+    return rr * slope2 - slope2;
+}
+
+static GeglRectangle
+calculate_dab_roi (float x,
+                   float y,
+                   float radius)
+{
+  int x0 = floor (x - radius);
+  int x1 = ceil (x + radius);
+  int y0 = floor (y - radius);
+  int y1 = ceil (y + radius);
+
+  return *GEGL_RECTANGLE (x0, y0, x1 - x0, y1 - y0);
+}
+
+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)
+{
+  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);
+    while (gegl_buffer_iterator_next (iter))
+      {
+        float *pixel = (float *)iter->data[0];
+        int iy, ix;
+
+        for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++)
+          {
+            float yy = (iy + 0.5f - y);
+            for (ix = iter->roi[0].x; ix < iter->roi[0].x +  iter->roi[0].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;
+
+                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 (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 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)
+{
+  GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
+  GeglBufferIterator *iter;
+  GeglRectangle       dabRect;
+  GimpComponentMask   component_mask = surface->component_mask;
+
+  const float one_over_radius2 = 1.0f / (radius * radius);
+  const double angle_rad = angle / 360 * 2 * M_PI;
+  const float cs = cos(angle_rad);
+  const float sn = sin(angle_rad);
+  float segment1_slope;
+  float segment2_slope;
+  float r_aa_start;
+  float mixbuf[4];
+
+  hardness = CLAMP (hardness, 0.0f, 1.0f);
+  segment1_slope = -(1.0f / hardness - 1.0f);
+  segment2_slope = -hardness / (1.0f - hardness);
+  aspect_ratio = MAX (1.0f, aspect_ratio);
+
+  r_aa_start = radius - 1.0f;
+  r_aa_start = MAX (r_aa_start, 0);
+  r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio;
+
+  /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
+  dabRect = calculate_dab_roi (x, y, radius);
+  gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer));
+
+  if (dabRect.width <= 0 || dabRect.height <= 0)
+    return 0;
+
+  gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect);
+
+  iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
+                                   babl_format ("R'G'B'A float"),
+                                   GEGL_BUFFER_READWRITE,
+                                   GEGL_ABYSS_NONE);
+
+  while (gegl_buffer_iterator_next (iter))
+    {
+      float *pixel = (float *)iter->data[0];
+      int iy, ix;
+      for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++)
+        {
+          for (ix = iter->roi[0].x; ix < iter->roi[0].x +  iter->roi[0].width; ix++)
+            {
+              float rr, alpha, dst_alpha, a;
+              float *dst_pixel = (component_mask != GIMP_COMPONENT_MASK_ALL) ? mixbuf : pixel;
+              if (radius < 3.0f)
+                rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, 
r_aa_start);
+              else
+                rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2);
+              alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope) * opaque;
+              dst_alpha = pixel[ALPHA];
+              a = alpha * color_a + dst_alpha * (1.0f - alpha);
+
+              if (a > 0.0f)
+                {
+                  float a_term = dst_alpha * (1.0f - alpha);
+                  dst_pixel[RED]   = (color_r * alpha * color_a + pixel[RED] * a_term) / a;
+                  dst_pixel[GREEN] = (color_g * alpha * color_a + pixel[GREEN] * a_term) / a;
+                  dst_pixel[BLUE]  = (color_b * alpha * color_a + pixel[BLUE] * a_term) / a;
+                }
+              dst_pixel[ALPHA] = a;
+
+              /* FIXME: Implement mypaint style lock-alpha mode */
+              /* FIXME: Implement colorize mode */
+
+              if (component_mask != GIMP_COMPONENT_MASK_ALL)
+                {
+                  if (component_mask & GIMP_COMPONENT_MASK_RED)
+                    pixel[RED] = mixbuf[RED];
+                  if (component_mask & GIMP_COMPONENT_MASK_GREEN)
+                    pixel[GREEN] = mixbuf[GREEN];
+                  if (component_mask & GIMP_COMPONENT_MASK_BLUE)
+                    pixel[BLUE] = mixbuf[BLUE];
+                  if (component_mask & GIMP_COMPONENT_MASK_ALPHA)
+                    pixel[ALPHA] = mixbuf[ALPHA];
+                }
+              pixel += 4;
+            }
+        }
+    }
+
+  return 1;
+}
+
+static void
+gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface)
+{
+
+}
+
+static void
+gimp_mypaint_surface_end_atomic (MyPaintSurface   *base_surface,
+                                 MyPaintRectangle *roi)
+{
+  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);
+}
+
+static void
+gimp_mypaint_surface_destroy (MyPaintSurface *base_surface)
+{
+  GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
+  g_object_unref (surface->buffer);
+  surface->buffer = NULL;
+}
+
+GimpMybrushSurface *
+gimp_mypaint_surface_new (GeglBuffer        *buffer,
+                          GimpComponentMask  component_mask)
+{
+  GimpMybrushSurface *surface = g_malloc0 (sizeof (GimpMybrushSurface));
+  mypaint_surface_init ((MyPaintSurface *)surface);
+  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->buffer = g_object_ref (buffer);
+  surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
+
+  return surface;
+}
+
+#endif
\ No newline at end of file
diff --git a/app/paint/gimpmybrushsurface.h b/app/paint/gimpmybrushsurface.h
new file mode 100644
index 0000000..124fbbc
--- /dev/null
+++ b/app/paint/gimpmybrushsurface.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_LIBMYPAINT
+
+#ifndef  __GIMP_MYBRUSH_SURFACE_H__
+#define  __GIMP_MYBRUSH_SURFACE_H__
+
+#include "paint-types.h"
+#include "core/gimpobject.h"
+
+typedef struct _GimpMybrushSurface GimpMybrushSurface;
+
+GimpMybrushSurface *
+gimp_mypaint_surface_new (GeglBuffer        *buffer,
+                          GimpComponentMask  component_mask);
+
+
+#endif  /*  __GIMP_MYBRUSH_SURFACE_H__  */
+
+#endif
diff --git a/app/tools/gimpmybrushoptions-gui.c b/app/tools/gimpmybrushoptions-gui.c
index ae3b6ee..b37fce7 100644
--- a/app/tools/gimpmybrushoptions-gui.c
+++ b/app/tools/gimpmybrushoptions-gui.c
@@ -168,6 +168,13 @@ gimp_mybrush_options_gui (GimpToolOptions *tool_options)
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
+  /* opaque */
+  scale = gimp_prop_spin_scale_new (config, "opaque",
+                                    _("Base Opacity"),
+                                    0.1, 1.0, 2);
+  gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+  gtk_widget_show (scale);
+
   /* hardness */
   scale = gimp_prop_spin_scale_new (config, "hardness",
                                     _("Hardness"),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]