[gimp/wip/paintcore-loops] app: refactor gimppaintcore-loops to coalesce iteration



commit d5cbdb48021298c830f8c95aace074b736bddb0b
Author: Ell <ell_se yahoo com>
Date:   Sat Apr 14 19:02:21 2018 -0400

    app: refactor gimppaintcore-loops to coalesce iteration
    
    WIP

 app/paint/gimppaintcore-loops.cc | 1190 ++++++++++++++++++++++++++++----------
 app/paint/gimppaintcore-loops.h  |  116 +++--
 app/paint/gimppaintcore.c        |   75 ++--
 3 files changed, 1008 insertions(+), 373 deletions(-)
---
diff --git a/app/paint/gimppaintcore-loops.cc b/app/paint/gimppaintcore-loops.cc
index c02335d..beb9ec8 100644
--- a/app/paint/gimppaintcore-loops.cc
+++ b/app/paint/gimppaintcore-loops.cc
@@ -34,211 +34,922 @@ extern "C"
 
 #include "gimppaintcore-loops.h"
 
+} /* extern "C" */
+
 
 #define MIN_PARALLEL_SUB_SIZE 64
 #define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE)
 
 
-void
-combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
-                                   gint               mask_x_offset,
-                                   gint               mask_y_offset,
-                                   GeglBuffer        *canvas_buffer,
-                                   gint               x_offset,
-                                   gint               y_offset,
-                                   gfloat             opacity,
-                                   gboolean           stipple)
+enum
 {
-  GeglRectangle roi;
+  ALGORITHM_PAINT_BUF  = 1u << 31,
+  ALGORITHM_PAINT_MASK = 1u << 30,
+  ALGORITHM_STIPPLE    = 1u << 29
+};
 
-  const gint   mask_stride       = gimp_temp_buf_get_width (paint_mask);
-  const gint   mask_start_offset = mask_y_offset * mask_stride + mask_x_offset;
-  const Babl  *mask_format       = gimp_temp_buf_get_format (paint_mask);
-  gint         width;
-  gint         height;
 
-  width  = gimp_temp_buf_get_width (paint_mask);
-  height = gimp_temp_buf_get_height (paint_mask);
+template <class T>
+struct identity
+{
+  using type = T;
+};
 
-  roi.x = x_offset;
-  roi.y = y_offset;
-  roi.width  = width - mask_x_offset;
-  roi.height = height - mask_y_offset;
 
-  gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
-                                 [=] (const GeglRectangle *area)
-    {
-      GeglBufferIterator *iter;
+template <class Visitor,
+          class Algorithm>
+static inline void
+dispatch (Visitor                         visitor,
+          const GimpPaintCoreLoopsParams *params,
+          GimpPaintCoreLoopsAlgorithm     algorithms,
+          identity<Algorithm>             algorithm)
+{
+  visitor (algorithm);
+}
 
-      iter = gegl_buffer_iterator_new (canvas_buffer, area, 0,
-                                       babl_format ("Y float"),
-                                       GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+template <class Algorithm,
+          class Dispatch,
+          gboolean = (Algorithm::filter & Dispatch::mask) == Dispatch::mask>
+struct dispatch_impl
+{
+  template <class    Visitor,
+            class... DispatchRest>
+  static void
+  apply (Visitor                         visitor,
+         const GimpPaintCoreLoopsParams *params,
+         GimpPaintCoreLoopsAlgorithm     algorithms,
+         identity<Algorithm>             algorithm,
+         Dispatch                        disp,
+         DispatchRest...                 disp_rest)
+  {
+    disp (
+      [&] (auto algorithm)
+      {
+        dispatch (visitor, params, algorithms, algorithm, disp_rest...);
+      },
+      params, algorithms, algorithm);
+  }
+};
+
+template <class Algorithm,
+          class Dispatch>
+struct dispatch_impl<Algorithm, Dispatch, TRUE>
+{
+  template <class    Visitor,
+            class... DispatchRest>
+  static void
+  apply (Visitor                         visitor,
+         const GimpPaintCoreLoopsParams *params,
+         GimpPaintCoreLoopsAlgorithm     algorithms,
+         identity<Algorithm>             algorithm,
+         Dispatch                        disp,
+         DispatchRest...                 disp_rest)
+  {
+    dispatch (visitor, params, algorithms, algorithm, disp_rest...);
+  }
+};
+
+template <class    Visitor,
+          class    Algorithm,
+          class    Dispatch,
+          class... DispatchRest>
+static inline void
+dispatch (Visitor                         visitor,
+          const GimpPaintCoreLoopsParams *params,
+          GimpPaintCoreLoopsAlgorithm     algorithms,
+          identity<Algorithm>             algorithm,
+          Dispatch                        disp,
+          DispatchRest...                 disp_rest)
+{
+  dispatch_impl<Algorithm, Dispatch>::apply (
+    visitor, params, algorithms, algorithm, disp, disp_rest...);
+}
 
-      if (stipple)
-        {
-          if (mask_format == babl_format ("Y u8"))
-            {
-              const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
-              mask_data += mask_start_offset;
 
-              while (gegl_buffer_iterator_next (iter))
-                {
-                  gfloat *out_pixel = (gfloat *)iter->data[0];
-                  int iy, ix;
+static inline gfloat
+value_to_float (guint8 value)
+{
+  return value / 255.0f;
+}
 
-                  for (iy = 0; iy < iter->roi[0].height; iy++)
-                    {
-                      int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
-                      const guint8 *mask_pixel = &mask_data[mask_offset];
+static inline gfloat
+value_to_float (gfloat value)
+{
+  return value;
+}
 
-                      for (ix = 0; ix < iter->roi[0].width; ix++)
-                        {
-                          out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel / 255.0f) * opacity;
+template <class T>
+static inline gfloat
+value_to_float (T value) = delete;
 
-                          mask_pixel += 1;
-                          out_pixel  += 1;
-                        }
-                    }
-                }
-            }
-          else if (mask_format == babl_format ("Y float"))
-            {
-              const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
-              mask_data += mask_start_offset;
 
-              while (gegl_buffer_iterator_next (iter))
-                {
-                  gfloat *out_pixel = (gfloat *)iter->data[0];
-                  int iy, ix;
+struct AlgorithmBase
+{
+  static constexpr guint          filter                 = 0;
+
+  static constexpr gint           canvas_buffer_iterator = -1;
+  static constexpr GeglAccessMode canvas_buffer_access   = {};
+
+  static constexpr gint           n_iterators            = 0;
+
+  explicit
+  AlgorithmBase (const GimpPaintCoreLoopsParams *params)
+  {
+  }
+
+  template <class Derived>
+  struct State
+  {
+  };
+
+  template <class Derived>
+  void
+  init (const GimpPaintCoreLoopsParams *params,
+        State<Derived>                 *state,
+        GeglBufferIterator             *iter,
+        const GeglRectangle            *roi,
+        const GeglRectangle            *area) const
+  {
+  }
+
+  template <class Derived>
+  void
+  init_step (const GimpPaintCoreLoopsParams *params,
+             State<Derived>                 *state,
+             GeglBufferIterator             *iter,
+             const GeglRectangle            *roi,
+             const GeglRectangle            *area) const
+  {
+  }
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+  }
+};
+
+template <template <class Base> class TemplateAlgorithm,
+          guint                       Mask>
+struct BasicDispatch
+{
+  static constexpr guint mask = Mask;
+
+  template <class Visitor,
+            class Algorithm>
+  void
+  operator () (Visitor                         visitor,
+               const GimpPaintCoreLoopsParams *params,
+               GimpPaintCoreLoopsAlgorithm     algorithms,
+               identity<Algorithm>             algorithm) const
+  {
+    visitor (identity<TemplateAlgorithm<Algorithm>> ());
+  }
+};
+
+template <template <class Base> class TemplateAlgorithm,
+          guint                       Mask,
+          class...                    Dependencies>
+struct AlgorithmDispatch
+{
+  static constexpr guint mask = Mask;
+
+  template <class Visitor,
+            class Algorithm>
+  void
+  operator () (Visitor                         visitor,
+               const GimpPaintCoreLoopsParams *params,
+               GimpPaintCoreLoopsAlgorithm     algorithms,
+               identity<Algorithm>             algorithm) const
+  {
+    if ((algorithms & mask) == mask)
+      {
+        dispatch (
+          [&] (auto algorithm)
+          {
+            using NewAlgorithm = typename decltype (algorithm)::type;
+
+            visitor (identity<TemplateAlgorithm<NewAlgorithm>> ());
+          },
+          params, algorithms, algorithm, Dependencies ()...);
+      }
+    else
+      {
+        visitor (algorithm);
+      }
+  }
+};
+
+template <class Base>
+struct PaintBuf : Base
+{
+  using paint_type = gfloat;
 
-                  for (iy = 0; iy < iter->roi[0].height; iy++)
-                    {
-                      int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
-                      const gfloat *mask_pixel = &mask_data[mask_offset];
+  static constexpr guint filter = Base::filter | ALGORITHM_PAINT_BUF;
 
-                      for (ix = 0; ix < iter->roi[0].width; ix++)
-                        {
-                          out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel) * opacity;
+  gint        paint_stride;
+  paint_type *paint_data;
 
-                          mask_pixel += 1;
-                          out_pixel  += 1;
-                        }
-                    }
-                }
-            }
-          else
-            {
-              g_warning("Mask format not supported: %s", babl_get_name (mask_format));
-            }
-        }
-      else
-        {
-          if (mask_format == babl_format ("Y u8"))
-            {
-              const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
-              mask_data += mask_start_offset;
+  explicit
+  PaintBuf (const GimpPaintCoreLoopsParams *params) :
+    Base (params)
+  {
+    paint_stride = gimp_temp_buf_get_width (params->paint_buf) * 4;
+    paint_data   = (paint_type *) gimp_temp_buf_get_data (params->paint_buf);
+  }
+};
 
-              while (gegl_buffer_iterator_next (iter))
-                {
-                  gfloat *out_pixel = (gfloat *)iter->data[0];
-                  int iy, ix;
+static BasicDispatch<PaintBuf, ALGORITHM_PAINT_BUF> dispatch_paint_buf;
 
-                  for (iy = 0; iy < iter->roi[0].height; iy++)
-                    {
-                      int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
-                      const guint8 *mask_pixel = &mask_data[mask_offset];
+template <class Base,
+          class MaskType>
+struct PaintMask : Base
+{
+  using mask_type = MaskType;
+
+  static constexpr guint filter = Base::filter | ALGORITHM_PAINT_MASK;
+
+  gint             mask_stride;
+  const mask_type *mask_data;
+
+  explicit
+  PaintMask (const GimpPaintCoreLoopsParams *params) :
+    Base (params)
+  {
+    mask_stride = gimp_temp_buf_get_width (params->paint_mask);
+    mask_data   =
+      (const mask_type *) gimp_temp_buf_get_data (params->paint_mask) +
+      params->paint_mask_offset_y * mask_stride                       +
+      params->paint_mask_offset_x;
+  }
+};
+
+struct DispatchPaintMask
+{
+  static constexpr guint mask = ALGORITHM_PAINT_MASK;
+
+  template <class Visitor,
+            class Algorithm>
+  void
+  operator () (Visitor                         visitor,
+               const GimpPaintCoreLoopsParams *params,
+               GimpPaintCoreLoopsAlgorithm     algorithms,
+               identity<Algorithm>             algorithm) const
+  {
+    const Babl *mask_format = gimp_temp_buf_get_format (params->paint_mask);
+
+    if (mask_format == babl_format ("Y u8"))
+      visitor (identity<PaintMask<Algorithm, guint8>> ());
+    else if (mask_format == babl_format ("Y float"))
+      visitor (identity<PaintMask<Algorithm, gfloat>> ());
+    else
+      g_warning ("Mask format not supported: %s", babl_get_name (mask_format));
+  }
+} static dispatch_paint_mask;
+
+template <class    Base,
+          gboolean StippleFlag>
+struct Stipple : Base
+{
+  static constexpr guint    filter  = Base::filter | ALGORITHM_STIPPLE;
 
-                      for (ix = 0; ix < iter->roi[0].width; ix++)
-                        {
-                          if (opacity > out_pixel[0])
-                            out_pixel[0] += (opacity - out_pixel[0]) * (*mask_pixel / 255.0f) * opacity;
+  static constexpr gboolean stipple = StippleFlag;
 
-                          mask_pixel += 1;
-                          out_pixel  += 1;
-                        }
-                    }
-                }
-            }
-          else if (mask_format == babl_format ("Y float"))
+  using Base::Base;
+};
+
+struct DispatchStipple
+{
+  static constexpr guint mask = ALGORITHM_STIPPLE;
+
+  template <class Visitor,
+            class Algorithm>
+  void
+  operator () (Visitor                         visitor,
+               const GimpPaintCoreLoopsParams *params,
+               GimpPaintCoreLoopsAlgorithm     algorithms,
+               identity<Algorithm>             algorithm) const
+  {
+    if (params->stipple)
+      visitor (identity<Stipple<Algorithm, TRUE>> ());
+    else
+      visitor (identity<Stipple<Algorithm, FALSE>> ());
+  }
+} static dispatch_stipple;
+
+template <class Base,
+          guint Access>
+struct CanvasBufferIterator : Base
+{
+  static constexpr gint  canvas_buffer_iterator        =
+    Base::canvas_buffer_iterator < 0 ? Base::n_iterators :
+                                       Base::canvas_buffer_iterator;
+  static constexpr GeglAccessMode canvas_buffer_access =
+    (GeglAccessMode) (+Base::canvas_buffer_access |  Access);
+  static constexpr gint  n_iterators                   =
+    Base::canvas_buffer_iterator < 0 ? Base::n_iterators + 1:
+                                       Base::n_iterators;
+
+  using Base::Base;
+
+  template <class Derived>
+  using State = typename Base::template State<Derived>;
+
+  template <class Derived>
+  void
+  init (const GimpPaintCoreLoopsParams *params,
+        State<Derived>                 *state,
+        GeglBufferIterator             *iter,
+        const GeglRectangle            *roi,
+        const GeglRectangle            *area) const
+  {
+    Base::init (params, state, iter, roi, area);
+
+    if (Base::canvas_buffer_iterator < 0)
+      {
+        gegl_buffer_iterator_add (iter, params->canvas_buffer, area, 0,
+                                  babl_format ("Y float"),
+                                  Derived::canvas_buffer_access,
+                                  GEGL_ABYSS_NONE);
+      }
+  }
+};
+
+template <class Base>
+struct CombinePaintMaskToCanvasMaskToPaintBufAlpha : CanvasBufferIterator<Base,
+                                                                          GEGL_BUFFER_READWRITE>
+{
+  using base_type = CanvasBufferIterator<Base, GEGL_BUFFER_READWRITE>;
+  using mask_type = typename base_type::mask_type;
+
+  static constexpr guint filter =
+    base_type::filter                                                 |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA  |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
+
+  using base_type::base_type;
+
+  template <class Derived>
+  struct State : base_type::template State<Derived>
+  {
+    gfloat *canvas_pixel;
+  };
+
+  template <class Derived>
+  void
+  init_step (const GimpPaintCoreLoopsParams *params,
+             State<Derived>                 *state,
+             GeglBufferIterator             *iter,
+             const GeglRectangle            *roi,
+             const GeglRectangle            *area) const
+  {
+    base_type::init_step (params, state, iter, roi, area);
+
+    state->canvas_pixel =
+      (gfloat *) iter->data[base_type::canvas_buffer_iterator];
+  }
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+    base_type::process_row (params, state, iter, roi, area, y);
+
+    gint             mask_offset  = (y + iter->roi[0].y - roi->y) * this->mask_stride +
+                                    iter->roi[0].x - roi->x;
+    const mask_type *mask_pixel   = &this->mask_data[mask_offset];
+    gint             paint_offset = (y + iter->roi[0].y - roi->y) * this->paint_stride +
+                                    (iter->roi[0].x - roi->x) * 4;
+    gfloat          *paint_pixel  = &this->paint_data[paint_offset];
+    gint             x;
+
+    for (x = 0; x < iter->roi[0].width; x++)
+      {
+        if (base_type::stipple)
+          {
+            state->canvas_pixel[0] += (1.0 - state->canvas_pixel[0])  *
+                                      value_to_float (*mask_pixel)    *
+                                      params->paint_opacity;
+          }
+        else
+          {
+            if (params->paint_opacity > state->canvas_pixel[0])
+              {
+                state->canvas_pixel[0] += (params->paint_opacity - state->canvas_pixel[0]) *
+                                           value_to_float (*mask_pixel)                    *
+                                           params->paint_opacity;
+              }
+          }
+
+        paint_pixel[3] *= state->canvas_pixel[0];
+
+        mask_pixel          += 1;
+        state->canvas_pixel += 1;
+        paint_pixel         += 4;
+      }
+  }
+};
+
+static AlgorithmDispatch<
+  CombinePaintMaskToCanvasMaskToPaintBufAlpha,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK |
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA,
+  decltype (dispatch_paint_buf),
+  decltype (dispatch_paint_mask),
+  decltype (dispatch_stipple)
+>
+dispatch_combine_paint_mask_to_canvas_mask_to_paint_buf_alpha;
+
+template <class Base>
+struct CombinePaintMaskToCanvasMask : CanvasBufferIterator<Base,
+                                                           GEGL_BUFFER_READWRITE>
+{
+  using base_type = CanvasBufferIterator<Base, GEGL_BUFFER_READWRITE>;
+  using mask_type = typename base_type::mask_type;
+
+  static constexpr guint filter =
+    base_type::filter                                                 |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
+
+  using base_type::base_type;
+
+  template <class Derived>
+  struct State : base_type::template State<Derived>
+  {
+    gfloat *canvas_pixel;
+  };
+
+  template <class Derived>
+  void
+  init_step (const GimpPaintCoreLoopsParams *params,
+             State<Derived>                 *state,
+             GeglBufferIterator             *iter,
+             const GeglRectangle            *roi,
+             const GeglRectangle            *area) const
+  {
+    base_type::init_step (params, state, iter, roi, area);
+
+    state->canvas_pixel =
+      (gfloat *) iter->data[base_type::canvas_buffer_iterator];
+  }
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+    base_type::process_row (params, state, iter, roi, area, y);
+
+    gint             mask_offset = (y + iter->roi[0].y - roi->y) * this->mask_stride +
+                                   iter->roi[0].x - roi->x;
+    const mask_type *mask_pixel  = &this->mask_data[mask_offset];
+    gint             x;
+
+    for (x = 0; x < iter->roi[0].width; x++)
+      {
+        if (base_type::stipple)
+          {
+            state->canvas_pixel[0] += (1.0 - state->canvas_pixel[0]) *
+                                      value_to_float (*mask_pixel)   *
+                                      params->paint_opacity;
+          }
+        else
+          {
+            if (params->paint_opacity > state->canvas_pixel[0])
+              {
+                state->canvas_pixel[0] += (params->paint_opacity - state->canvas_pixel[0]) *
+                                          value_to_float (*mask_pixel)                     *
+                                          params->paint_opacity;
+              }
+          }
+
+        mask_pixel          += 1;
+        state->canvas_pixel += 1;
+      }
+  }
+};
+
+static AlgorithmDispatch<
+  CombinePaintMaskToCanvasMask,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK,
+  decltype (dispatch_paint_mask),
+  decltype (dispatch_stipple)
+>
+dispatch_combine_paint_mask_to_canvas_mask;
+
+template <class Base>
+struct CanvasBufferToPaintBufAlpha : CanvasBufferIterator<Base,
+                                                          GEGL_BUFFER_READ>
+{
+  using base_type = CanvasBufferIterator<Base, GEGL_BUFFER_READ>;
+
+  static constexpr guint filter =
+    base_type::filter                                                |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
+
+  using base_type::base_type;
+
+  template <class Derived>
+  struct State : base_type::template State<Derived>
+  {
+    const gfloat *canvas_pixel;
+  };
+
+  template <class Derived>
+  void
+  init_step (const GimpPaintCoreLoopsParams *params,
+             State<Derived>                 *state,
+             GeglBufferIterator             *iter,
+             const GeglRectangle            *roi,
+             const GeglRectangle            *area) const
+  {
+    base_type::init_step (params, state, iter, roi, area);
+
+    state->canvas_pixel =
+      (const gfloat *) iter->data[base_type::canvas_buffer_iterator];
+  }
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+    base_type::process_row (params, state, iter, roi, area, y);
+
+    /* Copy the canvas buffer in rect to the paint buffer's alpha channel */
+
+    gint    paint_offset = (y + iter->roi[0].y - roi->y) * this->paint_stride +
+                           (iter->roi[0].x - roi->x) * 4;
+    gfloat *paint_pixel  = &this->paint_data[paint_offset];
+    gint    x;
+
+    for (x = 0; x < iter->roi[0].width; x++)
+      {
+        paint_pixel[3] *= *state->canvas_pixel;
+
+        state->canvas_pixel += 1;
+        paint_pixel         += 4;
+      }
+  }
+};
+
+static AlgorithmDispatch<
+  CanvasBufferToPaintBufAlpha,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA,
+  decltype (dispatch_paint_buf)
+>
+dispatch_canvas_buffer_to_paint_buf_alpha;
+
+template <class Base>
+struct PaintMaskToPaintBuffer : Base
+{
+  using mask_type = typename Base::mask_type;
+
+  static constexpr guint filter =
+    Base::filter |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
+
+  explicit
+  PaintMaskToPaintBuffer (const GimpPaintCoreLoopsParams *params) :
+    Base (params)
+  {
+    /* Validate that the paint buffer is withing the bounds of the paint mask */
+    g_return_if_fail (gimp_temp_buf_get_width (params->paint_buf) <=
+                      gimp_temp_buf_get_width (params->paint_mask) -
+                      params->paint_mask_offset_x);
+    g_return_if_fail (gimp_temp_buf_get_height (params->paint_buf) <=
+                      gimp_temp_buf_get_height (params->paint_mask) -
+                      params->paint_mask_offset_y);
+  }
+
+  template <class Derived>
+  using State = typename Base::template State<Derived>;
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+    Base::process_row (params, state, iter, roi, area, y);
+
+    gint             paint_offset = (y + iter->roi[0].y - roi->y) * this->paint_stride +
+                                    (iter->roi[0].x - roi->x)     * 4;
+    gfloat          *paint_pixel  = &this->paint_data[paint_offset];
+    gint             mask_offset  = (y + iter->roi[0].y - roi->y) * this->mask_stride +
+                                    iter->roi[0].x - roi->x;
+    const mask_type *mask_pixel   = &this->mask_data[mask_offset];
+    gint             x;
+
+    for (x = 0; x < iter->roi[0].width; x++)
+      {
+        paint_pixel[3] *= value_to_float (*mask_pixel) * params->paint_opacity;
+
+        mask_pixel  += 1;
+        paint_pixel += 4;
+      }
+  }
+};
+
+static AlgorithmDispatch<
+  PaintMaskToPaintBuffer,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER,
+  decltype (dispatch_paint_buf),
+  decltype (dispatch_paint_mask)
+>
+dispatch_paint_mask_to_paint_buffer;
+
+template <class Base>
+struct DoLayerBlend : Base
+{
+  static constexpr guint filter =
+    Base::filter |
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND;
+
+  static constexpr gint iterator_base = Base::n_iterators;
+  static constexpr gint n_iterators   = Base::n_iterators + 3;
+
+  const Babl             *iterator_format;
+  GimpOperationLayerMode  layer_mode;
+
+  explicit
+  DoLayerBlend (const GimpPaintCoreLoopsParams *params) :
+    Base (params)
+  {
+    layer_mode.layer_mode          = params->paint_mode;
+    layer_mode.opacity             = params->image_opacity;
+    layer_mode.function            = gimp_layer_mode_get_function (params->paint_mode);
+    layer_mode.blend_function      = gimp_layer_mode_get_blend_function (params->paint_mode);
+    layer_mode.blend_space         = gimp_layer_mode_get_blend_space (params->paint_mode);
+    layer_mode.composite_space     = gimp_layer_mode_get_composite_space (params->paint_mode);
+    layer_mode.composite_mode      = gimp_layer_mode_get_paint_composite_mode (params->paint_mode);
+    layer_mode.real_composite_mode = layer_mode.composite_mode;
+
+    iterator_format = gimp_layer_mode_get_format (params->paint_mode,
+                                                  layer_mode.composite_space,
+                                                  layer_mode.blend_space,
+                                                  gimp_temp_buf_get_format (params->paint_buf));
+
+    g_return_if_fail (gimp_temp_buf_get_format (params->paint_buf) == iterator_format);
+  }
+
+  template <class Derived>
+  struct State : Base::template State<Derived>
+  {
+    GeglRectangle  process_roi;
+
+    gfloat        *out_pixel;
+    gfloat        *in_pixel;
+    gfloat        *mask_pixel;
+    gfloat        *paint_pixel;
+  };
+
+  template <class Derived>
+  void
+  init (const GimpPaintCoreLoopsParams *params,
+        State<Derived>                 *state,
+        GeglBufferIterator             *iter,
+        const GeglRectangle            *roi,
+        const GeglRectangle            *area) const
+  {
+    Base::init (params, state, iter, roi, area);
+
+    GeglRectangle mask_area = *area;
+
+    mask_area.x -= params->mask_offset_x;
+    mask_area.y -= params->mask_offset_y;
+
+    gegl_buffer_iterator_add (iter, params->dest_buffer, area, 0,
+                              iterator_format,
+                              GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+    gegl_buffer_iterator_add (iter, params->src_buffer, area, 0,
+                              iterator_format,
+                              GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+    if (params->mask_buffer)
+      {
+        gegl_buffer_iterator_add (iter, params->mask_buffer, &mask_area, 0,
+                                  babl_format ("Y float"),
+                                  GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+      }
+  }
+
+  template <class Derived>
+  void
+  init_step (const GimpPaintCoreLoopsParams *params,
+             State<Derived>                 *state,
+             GeglBufferIterator             *iter,
+             const GeglRectangle            *roi,
+             const GeglRectangle            *area) const
+  {
+    Base::init_step (params, state, iter, roi, area);
+
+    state->out_pixel  = (gfloat *) iter->data[iterator_base + 0];
+    state->in_pixel   = (gfloat *) iter->data[iterator_base + 1];
+    state->mask_pixel = NULL;
+
+    state->paint_pixel = this->paint_data                               +
+                         (iter->roi[0].y - roi->y) * this->paint_stride +
+                         (iter->roi[0].x - roi->x) * 4;
+
+    if (params->mask_buffer)
+      state->mask_pixel = (gfloat *) iter->data[iterator_base + 2];
+
+    state->process_roi.x      = iter->roi[0].x;
+    state->process_roi.width  = iter->roi[0].width;
+    state->process_roi.height = 1;
+  }
+
+  template <class Derived>
+  void
+  process_row (const GimpPaintCoreLoopsParams *params,
+               State<Derived>                 *state,
+               GeglBufferIterator             *iter,
+               const GeglRectangle            *roi,
+               const GeglRectangle            *area,
+               gint                            y) const
+  {
+    Base::process_row (params, state, iter, roi, area, y);
+
+    state->process_roi.y = iter->roi[0].y + y;
+
+    layer_mode.function ((GeglOperation*) &layer_mode,
+                         state->in_pixel,
+                         state->paint_pixel,
+                         state->mask_pixel,
+                         state->out_pixel,
+                         iter->roi[0].width,
+                         &state->process_roi,
+                         0);
+
+    state->in_pixel     += iter->roi[0].width * 4;
+    state->out_pixel    += iter->roi[0].width * 4;
+    if (params->mask_buffer)
+      state->mask_pixel += iter->roi[0].width;
+    state->paint_pixel  += this->paint_stride;
+  }
+};
+
+static AlgorithmDispatch<
+  DoLayerBlend,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND,
+  decltype (dispatch_paint_buf)
+>
+dispatch_do_layer_blend;
+
+void
+gimp_paint_core_loops_process (const GimpPaintCoreLoopsParams *params,
+                               GimpPaintCoreLoopsAlgorithm     algorithms)
+{
+  GeglRectangle roi;
+
+  if (params->paint_buf)
+    {
+      roi.x      = params->paint_buf_offset_x;
+      roi.y      = params->paint_buf_offset_y;
+      roi.width  = gimp_temp_buf_get_width  (params->paint_buf);
+      roi.height = gimp_temp_buf_get_height (params->paint_buf);
+    }
+  else
+    {
+      roi.x      = params->paint_buf_offset_x;
+      roi.y      = params->paint_buf_offset_y;
+      roi.width  = gimp_temp_buf_get_width (params->paint_mask) -
+                   params->paint_mask_offset_x;
+      roi.height = gimp_temp_buf_get_height (params->paint_mask) -
+                   params->paint_mask_offset_y;
+    }
+
+  dispatch (
+    [&] (auto algorithm_type)
+    {
+      using Algorithm = typename decltype (algorithm_type)::type;
+      using State     = typename Algorithm::template State<Algorithm>;
+
+      Algorithm algorithm (params);
+
+      gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
+                                     [=] (const GeglRectangle *area)
+        {
+          State state;
+          gint  y;
+
+          if (Algorithm::n_iterators > 0)
             {
-              const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
-              mask_data += mask_start_offset;
+              GeglBufferIterator *iter;
+
+              iter = gegl_buffer_iterator_empty_new ();
+
+              algorithm.init (params, &state, iter, &roi, area);
 
               while (gegl_buffer_iterator_next (iter))
                 {
-                  gfloat *out_pixel = (gfloat *)iter->data[0];
-                  int iy, ix;
+                  algorithm.init_step (params, &state, iter, &roi, area);
 
-                  for (iy = 0; iy < iter->roi[0].height; iy++)
+                  for (y = 0; y < iter->roi[0].height; y++)
                     {
-                      int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
-                      const gfloat *mask_pixel = &mask_data[mask_offset];
-
-                      for (ix = 0; ix < iter->roi[0].width; ix++)
-                        {
-                          if (opacity > out_pixel[0])
-                            out_pixel[0] += (opacity - out_pixel[0]) * (*mask_pixel) * opacity;
-
-                          mask_pixel += 1;
-                          out_pixel  += 1;
-                        }
+                      algorithm.process_row (params, &state,
+                                             iter, &roi, area, y);
                     }
                 }
             }
           else
             {
-              g_warning("Mask format not supported: %s", babl_get_name (mask_format));
+              GeglBufferIterator iter;
+
+              iter.roi[0] = *area;
+
+              algorithm.init      (params, &state, &iter, &roi, area);
+              algorithm.init_step (params, &state, &iter, &roi, area);
+
+              for (y = 0; y < iter.roi[0].height; y++)
+                {
+                  algorithm.process_row (params, &state,
+                                         &iter, &roi, area, y);
+                }
             }
-        }
-    });
+        });
+    },
+    params, algorithms, identity<AlgorithmBase> (),
+    dispatch_combine_paint_mask_to_canvas_mask_to_paint_buf_alpha,
+    dispatch_combine_paint_mask_to_canvas_mask,
+    dispatch_canvas_buffer_to_paint_buf_alpha,
+    dispatch_paint_mask_to_paint_buffer,
+    dispatch_do_layer_blend);
 }
 
 void
-canvas_buffer_to_paint_buf_alpha (GimpTempBuf  *paint_buf,
-                                  GeglBuffer   *canvas_buffer,
-                                  gint          x_offset,
-                                  gint          y_offset)
+combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
+                                   gint               mask_x_offset,
+                                   gint               mask_y_offset,
+                                   GeglBuffer        *canvas_buffer,
+                                   gint               x_offset,
+                                   gint               y_offset,
+                                   gfloat             opacity,
+                                   gboolean           stipple)
 {
-  /* Copy the canvas buffer in rect to the paint buffer's alpha channel */
-  GeglRectangle roi;
+  GimpPaintCoreLoopsParams params = {};
 
-  const guint paint_stride = gimp_temp_buf_get_width (paint_buf);
-  gfloat *paint_data       = (gfloat *) gimp_temp_buf_get_data (paint_buf);
+  params.canvas_buffer       = canvas_buffer;
 
-  roi.x = x_offset;
-  roi.y = y_offset;
-  roi.width  = gimp_temp_buf_get_width (paint_buf);
-  roi.height = gimp_temp_buf_get_height (paint_buf);
+  params.paint_buf_offset_x  = x_offset;
+  params.paint_buf_offset_y  = y_offset;
 
-  gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
-                                 [=] (const GeglRectangle *area)
-    {
-      GeglBufferIterator *iter;
+  params.paint_mask          = paint_mask;
+  params.paint_mask_offset_x = mask_x_offset;
+  params.paint_mask_offset_y = mask_y_offset;
 
-      iter = gegl_buffer_iterator_new (canvas_buffer, area, 0,
-                                       babl_format ("Y float"),
-                                       GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+  params.stipple             = stipple;
 
-      while (gegl_buffer_iterator_next (iter))
-        {
-          gfloat *canvas_pixel = (gfloat *)iter->data[0];
-          int iy, ix;
+  params.paint_opacity       = opacity;
 
-          for (iy = 0; iy < iter->roi[0].height; iy++)
-            {
-              int paint_offset = (iy + iter->roi[0].y - roi.y) * paint_stride + iter->roi[0].x - roi.x;
-              float *paint_pixel = &paint_data[paint_offset * 4];
+  gimp_paint_core_loops_process (
+    &params,
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK);
+}
 
-              for (ix = 0; ix < iter->roi[0].width; ix++)
-                {
-                  paint_pixel[3] *= *canvas_pixel;
+void
+canvas_buffer_to_paint_buf_alpha (GimpTempBuf  *paint_buf,
+                                  GeglBuffer   *canvas_buffer,
+                                  gint          x_offset,
+                                  gint          y_offset)
+{
+  GimpPaintCoreLoopsParams params = {};
 
-                  canvas_pixel += 1;
-                  paint_pixel  += 4;
-                }
-            }
-        }
-    });
+  params.canvas_buffer      = canvas_buffer;
+
+  params.paint_buf          = paint_buf;
+  params.paint_buf_offset_x = x_offset;
+  params.paint_buf_offset_y = y_offset;
+
+  gimp_paint_core_loops_process (
+    &params,
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA);
 }
 
 void
@@ -248,63 +959,19 @@ paint_mask_to_paint_buffer (const GimpTempBuf  *paint_mask,
                             GimpTempBuf        *paint_buf,
                             gfloat              paint_opacity)
 {
-  gint width  = gimp_temp_buf_get_width (paint_buf);
-  gint height = gimp_temp_buf_get_height (paint_buf);
+  GimpPaintCoreLoopsParams params = {};
 
-  const gint mask_stride       = gimp_temp_buf_get_width (paint_mask);
-  const gint mask_start_offset = mask_y_offset * mask_stride + mask_x_offset;
-  const Babl *mask_format      = gimp_temp_buf_get_format (paint_mask);
+  params.paint_buf           = paint_buf;
 
-  /* Validate that the paint buffer is withing the bounds of the paint mask */
-  g_return_if_fail (width <= gimp_temp_buf_get_width (paint_mask) - mask_x_offset);
-  g_return_if_fail (height <= gimp_temp_buf_get_height (paint_mask) - mask_y_offset);
+  params.paint_mask          = paint_mask;
+  params.paint_mask_offset_x = mask_x_offset;
+  params.paint_mask_offset_y = mask_y_offset;
 
-  gimp_parallel_distribute_range (height, MIN_PARALLEL_SUB_SIZE,
-                                  [=] (gint y, gint height)
-    {
-      int iy, ix;
-      gfloat *paint_pixel = (gfloat *)gimp_temp_buf_get_data (paint_buf) +
-                            y * width * 4;
-
-      if (mask_format == babl_format ("Y u8"))
-        {
-          const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
-          mask_data += mask_start_offset + y * mask_stride;
-
-          for (iy = 0; iy < height; iy++)
-            {
-              int mask_offset = iy * mask_stride;
-              const guint8 *mask_pixel = &mask_data[mask_offset];
+  params.paint_opacity       = paint_opacity;
 
-              for (ix = 0; ix < width; ix++)
-                {
-                  paint_pixel[3] *= (((gfloat)*mask_pixel) / 255.0f) * paint_opacity;
-
-                  mask_pixel  += 1;
-                  paint_pixel += 4;
-                }
-            }
-        }
-      else if (mask_format == babl_format ("Y float"))
-        {
-          const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
-          mask_data += mask_start_offset + y * mask_stride;
-
-          for (iy = 0; iy < height; iy++)
-            {
-              int mask_offset = iy * mask_stride;
-              const gfloat *mask_pixel = &mask_data[mask_offset];
-
-              for (ix = 0; ix < width; ix++)
-                {
-                  paint_pixel[3] *= (*mask_pixel) * paint_opacity;
-
-                  mask_pixel  += 1;
-                  paint_pixel += 4;
-                }
-            }
-        }
-    });
+  gimp_paint_core_loops_process (
+    &params,
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER);
 }
 
 void
@@ -319,99 +986,26 @@ do_layer_blend (GeglBuffer    *src_buffer,
                 gint           mask_y_offset,
                 GimpLayerMode  paint_mode)
 {
-  GeglRectangle           roi;
-  const Babl             *iterator_format;
-  guint                   paint_stride;
-  gfloat                 *paint_data;
-  GimpOperationLayerMode  layer_mode;
-
-  paint_stride = gimp_temp_buf_get_width (paint_buf);
-  paint_data   = (gfloat *) gimp_temp_buf_get_data (paint_buf);
-
-  layer_mode.layer_mode          = paint_mode;
-  layer_mode.opacity             = opacity;
-  layer_mode.function            = gimp_layer_mode_get_function (paint_mode);
-  layer_mode.blend_function      = gimp_layer_mode_get_blend_function (paint_mode);
-  layer_mode.blend_space         = gimp_layer_mode_get_blend_space (paint_mode);
-  layer_mode.composite_space     = gimp_layer_mode_get_composite_space (paint_mode);
-  layer_mode.composite_mode      = gimp_layer_mode_get_paint_composite_mode (paint_mode);
-  layer_mode.real_composite_mode = layer_mode.composite_mode;
-
-  iterator_format = gimp_layer_mode_get_format (paint_mode,
-                                                layer_mode.composite_space,
-                                                layer_mode.blend_space,
-                                                gimp_temp_buf_get_format (paint_buf));
-
-  roi.x = x_offset;
-  roi.y = y_offset;
-  roi.width  = gimp_temp_buf_get_width (paint_buf);
-  roi.height = gimp_temp_buf_get_height (paint_buf);
-
-  g_return_if_fail (gimp_temp_buf_get_format (paint_buf) == iterator_format);
+  GimpPaintCoreLoopsParams params = {};
 
-  gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
-                                 [=] (const GeglRectangle *area)
-    {
-      GeglBufferIterator     *iter;
-      GeglRectangle           mask_area = *area;
-
-      mask_area.x -= mask_x_offset;
-      mask_area.y -= mask_y_offset;
-
-      iter = gegl_buffer_iterator_new (dst_buffer, area, 0,
-                                       iterator_format,
-                                       GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
-      gegl_buffer_iterator_add (iter, src_buffer, area, 0,
-                                iterator_format,
-                                GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+  params.paint_buf          = paint_buf;
+  params.paint_buf_offset_x = x_offset;
+  params.paint_buf_offset_y = y_offset;
 
-      if (mask_buffer)
-        {
-          gegl_buffer_iterator_add (iter, mask_buffer, &mask_area, 0,
-                                    babl_format ("Y float"),
-                                    GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
-        }
-
-      while (gegl_buffer_iterator_next (iter))
-        {
-          GeglRectangle  process_roi;
-          gfloat        *out_pixel  = (gfloat *) iter->data[0];
-          gfloat        *in_pixel   = (gfloat *) iter->data[1];
-          gfloat        *mask_pixel = NULL;
-          gfloat        *paint_pixel;
-          gint           iy;
+  params.src_buffer         = src_buffer;
+  params.dest_buffer        = dst_buffer;
 
-          paint_pixel = paint_data + ((iter->roi[0].y - roi.y) * paint_stride + iter->roi[0].x - roi.x) * 4;
+  params.mask_buffer        = mask_buffer;
+  params.mask_offset_x      = mask_x_offset;
+  params.mask_offset_y      = mask_y_offset;
 
-          if (mask_buffer)
-            mask_pixel  = (gfloat *)iter->data[2];
+  params.image_opacity      = opacity;
 
-          process_roi.x = iter->roi[0].x;
-          process_roi.width  = iter->roi[0].width;
-          process_roi.height = 1;
+  params.paint_mode         = paint_mode;
 
-          for (iy = 0; iy < iter->roi[0].height; iy++)
-            {
-              process_roi.y = iter->roi[0].y + iy;
-
-              layer_mode.function ((GeglOperation*) &layer_mode,
-                                   in_pixel,
-                                   paint_pixel,
-                                   mask_pixel,
-                                   out_pixel,
-                                   iter->roi[0].width,
-                                   &process_roi,
-                                   0);
-
-              in_pixel    += iter->roi[0].width * 4;
-              out_pixel   += iter->roi[0].width * 4;
-              if (mask_buffer)
-                mask_pixel  += iter->roi[0].width;
-              paint_pixel += paint_stride * 4;
-            }
-        }
-    });
+  gimp_paint_core_loops_process (
+    &params,
+    GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND);
 }
 
 void
@@ -467,5 +1061,3 @@ mask_components_onto (GeglBuffer        *src_buffer,
         }
     });
 }
-
-} /* extern "C" */
diff --git a/app/paint/gimppaintcore-loops.h b/app/paint/gimppaintcore-loops.h
index 746d2a5..1e2c1fd 100644
--- a/app/paint/gimppaintcore-loops.h
+++ b/app/paint/gimppaintcore-loops.h
@@ -19,43 +19,85 @@
 #define __GIMP_PAINT_CORE_LOOPS_H__
 
 
-void combine_paint_mask_to_canvas_mask  (const GimpTempBuf *paint_mask,
-                                         gint               mask_x_offset,
-                                         gint               mask_y_offset,
-                                         GeglBuffer        *canvas_buffer,
-                                         gint               x_offset,
-                                         gint               y_offset,
-                                         gfloat             opacity,
-                                         gboolean           stipple);
-
-void canvas_buffer_to_paint_buf_alpha   (GimpTempBuf  *paint_buf,
-                                         GeglBuffer   *canvas_buffer,
-                                         gint          x_offset,
-                                         gint          y_offset);
-
-void paint_mask_to_paint_buffer         (const GimpTempBuf  *paint_mask,
-                                         gint                mask_x_offset,
-                                         gint                mask_y_offset,
-                                         GimpTempBuf        *paint_buf,
-                                         gfloat              paint_opacity);
-
-void do_layer_blend                     (GeglBuffer    *src_buffer,
-                                         GeglBuffer    *dst_buffer,
-                                         GimpTempBuf   *paint_buf,
-                                         GeglBuffer    *mask_buffer,
-                                         gfloat         opacity,
-                                         gint           x_offset,
-                                         gint           y_offset,
-                                         gint           mask_x_offset,
-                                         gint           mask_y_offset,
-                                         GimpLayerMode  paint_mode);
-
-void mask_components_onto               (GeglBuffer        *src_buffer,
-                                         GeglBuffer        *aux_buffer,
-                                         GeglBuffer        *dst_buffer,
-                                         GeglRectangle     *roi,
-                                         GimpComponentMask  mask,
-                                         gboolean           linear_mode);
+typedef enum
+{
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_NONE                              = 0,
+
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK = 1 << 0,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA  = 1 << 1,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER        = 1 << 2,
+  GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND                    = 1 << 3
+} GimpPaintCoreLoopsAlgorithm;
+
+
+typedef struct
+{
+  GeglBuffer        *canvas_buffer;
+
+  GimpTempBuf       *paint_buf;
+  gint               paint_buf_offset_x;
+  gint               paint_buf_offset_y;
+
+  const GimpTempBuf *paint_mask;
+  gint               paint_mask_offset_x;
+  gint               paint_mask_offset_y;
+
+  gboolean           stipple;
+
+  GeglBuffer        *src_buffer;
+  GeglBuffer        *dest_buffer;
+
+  GeglBuffer        *mask_buffer;
+  gint               mask_offset_x;
+  gint               mask_offset_y;
+
+  gdouble            paint_opacity;
+  gdouble            image_opacity;
+
+  GimpLayerMode      paint_mode;
+} GimpPaintCoreLoopsParams;
+
+
+void gimp_paint_core_loops_process      (const GimpPaintCoreLoopsParams *params,
+                                         GimpPaintCoreLoopsAlgorithm     algorithms);
+
+void combine_paint_mask_to_canvas_mask  (const GimpTempBuf              *paint_mask,
+                                         gint                            mask_x_offset,
+                                         gint                            mask_y_offset,
+                                         GeglBuffer                     *canvas_buffer,
+                                         gint                            x_offset,
+                                         gint                            y_offset,
+                                         gfloat                          opacity,
+                                         gboolean                        stipple);
+
+void canvas_buffer_to_paint_buf_alpha   (GimpTempBuf                    *paint_buf,
+                                         GeglBuffer                     *canvas_buffer,
+                                         gint                            x_offset,
+                                         gint                            y_offset);
+
+void paint_mask_to_paint_buffer         (const GimpTempBuf              *paint_mask,
+                                         gint                            mask_x_offset,
+                                         gint                            mask_y_offset,
+                                         GimpTempBuf                    *paint_buf,
+                                         gfloat                          paint_opacity);
+
+void do_layer_blend                     (GeglBuffer                     *src_buffer,
+                                         GeglBuffer                     *dst_buffer,
+                                         GimpTempBuf                    *paint_buf,
+                                         GeglBuffer                     *mask_buffer,
+                                         gfloat                          opacity,
+                                         gint                            x_offset,
+                                         gint                            y_offset,
+                                         gint                            mask_x_offset,
+                                         gint                            mask_y_offset,
+                                         GimpLayerMode                   paint_mode);
+
+void mask_components_onto               (GeglBuffer                     *src_buffer,
+                                         GeglBuffer                     *aux_buffer,
+                                         GeglBuffer                     *dst_buffer,
+                                         GeglRectangle                  *roi,
+                                         GimpComponentMask               mask,
+                                         gboolean                        linear_mode);
 
 
 #endif /* __GIMP_PAINT_CORE_LOOPS_H__ */
diff --git a/app/paint/gimppaintcore.c b/app/paint/gimppaintcore.c
index 79b8e48..f8e6986 100644
--- a/app/paint/gimppaintcore.c
+++ b/app/paint/gimppaintcore.c
@@ -864,77 +864,78 @@ gimp_paint_core_paste (GimpPaintCore            *core,
     }
   else
     {
-      GimpTempBuf *paint_buf = gimp_gegl_buffer_get_temp_buf (core->paint_buffer);
-      GeglBuffer  *dest_buffer;
-      GeglBuffer  *src_buffer;
+      GimpPaintCoreLoopsParams    params = {};
+      GimpPaintCoreLoopsAlgorithm algorithms = GIMP_PAINT_CORE_LOOPS_ALGORITHM_NONE;
 
-      if (! paint_buf)
+      params.paint_buf          = gimp_gegl_buffer_get_temp_buf (core->paint_buffer);
+      params.paint_buf_offset_x = core->paint_buffer_x;
+      params.paint_buf_offset_y = core->paint_buffer_y;
+
+      if (! params.paint_buf)
         return;
 
       if (core->comp_buffer)
-        dest_buffer = core->comp_buffer;
+        params.dest_buffer = core->comp_buffer;
       else
-        dest_buffer = gimp_drawable_get_buffer (drawable);
+        params.dest_buffer = gimp_drawable_get_buffer (drawable);
 
       if (mode == GIMP_PAINT_CONSTANT)
         {
+          params.canvas_buffer = core->canvas_buffer;
+
           /* This step is skipped by the ink tool, which writes
            * directly to canvas_buffer
            */
           if (paint_mask != NULL)
             {
               /* Mix paint mask and canvas_buffer */
-              combine_paint_mask_to_canvas_mask (paint_mask,
-                                                 paint_mask_offset_x,
-                                                 paint_mask_offset_y,
-                                                 core->canvas_buffer,
-                                                 core->paint_buffer_x,
-                                                 core->paint_buffer_y,
-                                                 paint_opacity,
-                                                 GIMP_IS_AIRBRUSH (core));
+              params.paint_mask          = paint_mask;
+              params.paint_mask_offset_x = paint_mask_offset_x;
+              params.paint_mask_offset_y = paint_mask_offset_y;
+              params.stipple             = GIMP_IS_AIRBRUSH (core);
+              params.paint_opacity       = paint_opacity;
+
+              algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK;
             }
 
           /* Write canvas_buffer to paint_buf */
-          canvas_buffer_to_paint_buf_alpha (paint_buf,
-                                            core->canvas_buffer,
-                                            core->paint_buffer_x,
-                                            core->paint_buffer_y);
+          algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA;
 
           /* undo buf -> paint_buf -> dest_buffer */
-          src_buffer = core->undo_buffer;
+          params.src_buffer = core->undo_buffer;
         }
       else
         {
           g_return_if_fail (paint_mask);
 
           /* Write paint_mask to paint_buf, does not modify canvas_buffer */
-          paint_mask_to_paint_buffer (paint_mask,
-                                      paint_mask_offset_x,
-                                      paint_mask_offset_y,
-                                      paint_buf,
-                                      paint_opacity);
+          params.paint_mask          = paint_mask;
+          params.paint_mask_offset_x = paint_mask_offset_x;
+          params.paint_mask_offset_y = paint_mask_offset_y;
+          params.paint_opacity       = paint_opacity;
+
+          algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
 
           /* dest_buffer -> paint_buf -> dest_buffer */
           if (core->comp_buffer)
-            src_buffer = gimp_drawable_get_buffer (drawable);
+            params.src_buffer = gimp_drawable_get_buffer (drawable);
           else
-            src_buffer = dest_buffer;
+            params.src_buffer = params.dest_buffer;
         }
 
-      do_layer_blend (src_buffer,
-                      dest_buffer,
-                      paint_buf,
-                      core->mask_buffer,
-                      image_opacity,
-                      core->paint_buffer_x,
-                      core->paint_buffer_y,
-                      core->mask_x_offset,
-                      core->mask_y_offset,
-                      paint_mode);
+      params.mask_buffer   = core->mask_buffer;
+      params.mask_offset_x = core->mask_x_offset;
+      params.mask_offset_y = core->mask_y_offset;
+      params.image_opacity = image_opacity;
+      params.paint_mode    = paint_mode;
+
+      algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND;
+
+      gimp_paint_core_loops_process (&params, algorithms);
 
       if (core->comp_buffer)
         {
-          mask_components_onto (src_buffer,
+          mask_components_onto (params.src_buffer,
                                 core->comp_buffer,
                                 gimp_drawable_get_buffer (drawable),
                                 GEGL_RECTANGLE (core->paint_buffer_x,


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