summaryrefslogtreecommitdiffstats
path: root/app/paint/gimppaintcore-loops.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /app/paint/gimppaintcore-loops.cc
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/paint/gimppaintcore-loops.cc')
-rw-r--r--app/paint/gimppaintcore-loops.cc2268
1 files changed, 2268 insertions, 0 deletions
diff --git a/app/paint/gimppaintcore-loops.cc b/app/paint/gimppaintcore-loops.cc
new file mode 100644
index 0000000..c92370d
--- /dev/null
+++ b/app/paint/gimppaintcore-loops.cc
@@ -0,0 +1,2268 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 2013 Daniel Sabo
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+extern "C"
+{
+
+#include "paint-types.h"
+
+#include "operations/layer-modes/gimp-layer-modes.h"
+
+#include "core/gimptempbuf.h"
+
+#include "operations/gimpoperationmaskcomponents.h"
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+#include "gimppaintcore-loops.h"
+
+} /* extern "C" */
+
+
+#define PIXELS_PER_THREAD \
+ (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
+
+
+/* In order to avoid iterating over the same region of the same buffers
+ * multiple times, when calling more than one of the paint-core loop functions
+ * (hereafter referred to as "algorithms") in succession, we provide a single
+ * function, gimp_paint_core_loops_process(), which can be used to perform
+ * multiple algorithms in a row. This function takes a pointer to a
+ * GimpPaintCoreLoopsParams structure, providing the parameters for the
+ * algorithms, and a GimpPaintCoreLoopsAlgorithm bitset, which specifies the
+ * set of algorithms to run; currently, the algorithms are always run in a
+ * fixed order. For convenience, we provide public functions for the
+ * individual algorithms, but they're merely wrappers around
+ * gimp_paint_core_loops_process().
+ *
+ * We use some C++ magic to statically generate specialized versions of
+ * gimp_paint_core_loops_process() for all possible combinations of algorithms,
+ * and, where relevant, formats and input parameters, and to dispatch to the
+ * correct version at runtime.
+ *
+ * To achieve this, each algorithm provides two components:
+ *
+ * - The algorithm class template, which implements the algorithm, following
+ * a common interface. See the AlgorithmBase class for a description of
+ * the interface. Each algorithm class takes its base class as a template
+ * parameter, which allows us to construct a class hierarchy corresponding
+ * to a specific set of algorithms. Some classes in the hierarchy are not
+ * algorithms themselves, but are rather helpers, which provide some
+ * functionality to the algorithms further down the hierarchy, such as
+ * access to specific buffers.
+ *
+ * - A dispatch function, which takes the input parameters, the requested set
+ * of algorithms, the (type of) the current algorithm hierarchy, and a
+ * visitor object. The function calls the visitor with a (potentially)
+ * modified hierarchy, depending on the input. Ihe dispatch function for
+ * an algorithm checks if the requested set of algorithms contains a
+ * certain algorithm, adds the said algorithm to the hierarchy accordingly,
+ * and calls the visitor with the new hierarchy. See the AlgorithmDispatch
+ * class, which provides a dispatch-function implementation which
+ * algorithms can use instead of providing their own dispatch function.
+ *
+ * Helper classes in the hierarchy may also provide dispatch functions,
+ * which likewise modify the hierarchy based on the input parameters. For
+ * example, the dispatch_paint_mask() function adds a certain PaintMask
+ * specialization to the hierarchy, depending on the format of the paint
+ * mask buffer; this can be used to specialize algorithms based on the mask
+ * format; an algorithm that depends on the paint mask may dispatch through
+ * this function, before modifying the hierarchy itself.
+ *
+ * The dispatch() function is used to construct an algorithm hierarchy by
+ * dispatching through a list of functions. gimp_paint_core_loops_process()
+ * calls dispatch() with the full list of algorithm dispatch functions,
+ * receiving in return the algorithm hierarchy matching the input. It then
+ * uses the algorithm interface to perform the actual processing.
+ */
+
+
+enum
+{
+ ALGORITHM_PAINT_BUF = 1u << 31,
+ ALGORITHM_PAINT_MASK = 1u << 30,
+ ALGORITHM_STIPPLE = 1u << 29,
+ ALGORITHM_COMP_MASK = 1u << 28,
+ ALGORITHM_TEMP_COMP_MASK = 1u << 27,
+ ALGORITHM_COMP_BUFFER = 1u << 26,
+ ALGORITHM_TEMP_COMP_BUFFER = 1u << 25,
+ ALGORITHM_CANVAS_BUFFER_ITERATOR = 1u << 24,
+ ALGORITHM_MASK_BUFFER_ITERATOR = 1u << 23
+};
+
+
+template <class T>
+struct identity
+{
+ using type = T;
+};
+
+
+/* dispatch():
+ *
+ * Takes a list of dispatch function objects, and calls each of them, in order,
+ * with the same 'params' and 'algorithms' parameters, passing 'algorithm' as
+ * the input hierarchy to the first dispatch function, and passing the output
+ * hierarchy of the previous dispatch function as the input hierarchy for the
+ * next dispatch function. Calls 'visitor' with the output hierarchy of the
+ * last dispatch function.
+ *
+ * Each algorithm hierarchy should provide a 'filter' static data member, and
+ * each dispatch function object should provide a 'mask' static data member.
+ * If the bitwise-AND of the current hierarchy's 'filter' member and the
+ * current dispatch function's 'mask' member is equal to 'mask', the dispatch
+ * function is skipped. This can be used to make sure that a class appears
+ * only once in the hierarchy, even if its dispatch function is used multiple
+ * times, or to prevent an algorithm from being dispatched, if it cannot be
+ * used together with another algorithm.
+ */
+
+template <class Visitor,
+ class Algorithm>
+static inline void
+dispatch (Visitor visitor,
+ const GimpPaintCoreLoopsParams *params,
+ GimpPaintCoreLoopsAlgorithm algorithms,
+ identity<Algorithm> algorithm)
+{
+ visitor (algorithm);
+}
+
+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...);
+}
+
+
+/* value_to_float():
+ *
+ * Converts a component value to float.
+ */
+
+static inline gfloat
+value_to_float (guint8 value)
+{
+ return value / 255.0f;
+}
+
+static inline gfloat
+value_to_float (gfloat value)
+{
+ return value;
+}
+
+template <class T>
+static inline gfloat
+value_to_float (T value) = delete;
+
+
+/* AlgorithmBase:
+ *
+ * The base class of the algorithm hierarchy.
+ */
+
+struct AlgorithmBase
+{
+ /* Used to filter-out dispatch functions; see the description of dispatch().
+ * Algorithms that redefine 'filter' should bitwise-OR their filter with that
+ * of their base class.
+ */
+ static constexpr guint filter = 0;
+
+ /* The current maximal number of iterators used by the hierarchy. Algorithms
+ * should redefine 'max_n_iterators' by adding the maximal number of
+ * iterators they use to this value.
+ */
+ static constexpr gint max_n_iterators = 0;
+
+ /* Non-static data members should be initialized in the constructor, and
+ * should not be further modified.
+ */
+ explicit
+ AlgorithmBase (const GimpPaintCoreLoopsParams *params)
+ {
+ }
+
+ /* Algorithms should store their dynamic state in the 'State' member class
+ * template. This template will be instantiated with the most-derived type
+ * of the hierarchy, which allows an algorithm to depend on the properties of
+ * its descendants. Algorithms that provide their own 'State' class should
+ * derive it from the 'State' class of their base class, passing 'Derived' as
+ * the template argument.
+ *
+ * Algorithms can be run in parallel on multiple threads. In this case, each
+ * thread uses its own 'State' object, while the algorithm object itself is
+ * either shared, or is a copy of a shared algorithm object. Either way, the
+ * algorithm object itself is immutable, while the state object is mutable.
+ */
+ template <class Derived>
+ struct State
+ {
+ };
+
+ /* The 'init()' function is called once per state object before processing
+ * starts, and should initialize the state object, and, if necessary, the
+ * iterator.
+ *
+ * 'params' is the same parameter struct passed to the constructor. 'state'
+ * is the state object. 'iter' is the iterator; each distinct state object
+ * uses a distinct iterator; if the algorithm hierarchy doesn't use any
+ * iterator, 'iter' may be NULL. 'roi' is the full region to be processed.
+ * 'area' is the subregion to be processed by the current state object.
+ *
+ * An algorithm that overrides this function should call the 'init()'
+ * function of its base class first, using the same arguments.
+ */
+ template <class Derived>
+ void
+ init (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area) const
+ {
+ }
+
+ /* The 'init_step()' function is called once after each
+ * 'gegl_buffer_iterator_next()' call, and should perform any necessary
+ * initialization required before processing the current chunk.
+ *
+ * The parameters are the same as for 'init()', with the addition of 'rect',
+ * which is the area of the current chunk.
+ *
+ * An algorithm that overrides this function should call the 'init_step()'
+ * function of its base class first, using the same arguments.
+ */
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ }
+
+ /* The 'process_row()' function is called for each row in the current chunk,
+ * and should perform the actual processing.
+ *
+ * The parameters are the same as for 'init_step()', with the addition of
+ * 'y', which is the current row.
+ *
+ * An algorithm that overrides this function should call the 'process_row()'
+ * function of its base class first, using the same arguments.
+ */
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ }
+
+ /* The 'finalize_step()' function is called once per chunk after its
+ * processing is done, and should finalize any chunk-specific resources of
+ * the state object.
+ *
+ * 'params' is the same parameter struct passed to the constructor. 'state'
+ * is the state object.
+ *
+ * An algorithm that overrides this function should call the
+ * 'finalize_step()' function of its base class after performing its own
+ * finalization, using the same arguments.
+ */
+ template <class Derived>
+ void
+ finalize_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state) const
+ {
+ }
+
+ /* The 'finalize()' function is called once per state object after processing
+ * is done, and should finalize the state object.
+ *
+ * 'params' is the same parameter struct passed to the constructor. 'state'
+ * is the state object.
+ *
+ * An algorithm that overrides this function should call the 'finalize()'
+ * function of its base class after performing its own finalization, using
+ * the same arguments.
+ */
+ template <class Derived>
+ void
+ finalize (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state) const
+ {
+ }
+};
+
+
+/* BasicDispatch:
+ *
+ * A class template implementing a simple dispatch function object, which adds
+ * an algorithm to the hierarchy unconditionally. 'AlgorithmTemplate' is the
+ * alogithm class template (usually a helper class, rather than an actual
+ * algorithm), 'Mask' is the dispatch function mask, as described in
+ * 'dispatch()', and 'Dependencies' is a list of (types of) dispatch functions
+ * the algorithm depends on.
+ *
+ * Before adding the algorithm to the hierarchy, the hierarchy is augmented by
+ * dispatching through the list of dependencies, in order.
+ */
+
+template <template <class Base> class AlgorithmTemplate,
+ guint Mask,
+ class... Dependencies>
+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
+ {
+ dispatch (
+ [&] (auto algorithm)
+ {
+ using NewAlgorithm = typename decltype (algorithm)::type;
+
+ visitor (identity<AlgorithmTemplate<NewAlgorithm>> ());
+ },
+ params, algorithms, algorithm, Dependencies ()...);
+ }
+};
+
+template <template <class Base> class AlgorithmTemplate,
+ guint Mask>
+struct BasicDispatch<AlgorithmTemplate, Mask>
+{
+ 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<AlgorithmTemplate<Algorithm>> ());
+ }
+};
+
+
+/* AlgorithmDispatch:
+ *
+ * A class template implementing a dispatch function suitable for dispatching
+ * algorithms. 'AlgorithmTemplate' is the algorithm class template, 'Mask' is
+ * the dispatch function mask, as described in 'dispatch()', and 'Dependencies'
+ * is a list of (types of) dispatch functions the algorithm depends on, used as
+ * explained below.
+ *
+ * 'AlgorithmDispatch' adds the algorithm to the hierarchy if it's included in
+ * the set of requested algorithms; specifically, if the bitwise-AND of the
+ * requested-algorithms bitset and of 'Mask' is equal to 'Mask'.
+ *
+ * Before adding the algorithm to the hierarchy, the hierarchy is augmented by
+ * dispatching through the list of dependencies, in order.
+ */
+
+template <template <class Base> class AlgorithmTemplate,
+ 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<AlgorithmTemplate<NewAlgorithm>> ());
+ },
+ params, algorithms, algorithm, Dependencies ()...);
+ }
+ else
+ {
+ visitor (algorithm);
+ }
+ }
+};
+
+
+/* MandatoryAlgorithmDispatch:
+ *
+ * A class template implementing a dispatch function suitable for dispatching
+ * algorithms that must be included in all hierarchies. 'AlgorithmTemplate' is
+ * the algorithm class template, 'Mask' is the dispatch function mask, as
+ * described in 'dispatch()', and 'Dependencies' is a list of (types of)
+ * dispatch functions the algorithm depends on, used as explained below.
+ *
+ * 'MandatoryAlgorithmDispatch' verifies that the algorithm is included in the
+ * set of requested algorithms (specifically, that the bitwise-AND of the
+ * requested-algorithms bitset and of 'Mask' is equal to 'Mask'), and adds the
+ * it to the hierarchy unconditionally.
+ *
+ * Before adding the algorithm to the hierarchy, the hierarchy is augmented by
+ * dispatching through the list of dependencies, in order.
+ */
+
+template <template <class Base> class AlgorithmTemplate,
+ guint Mask,
+ class... Dependencies>
+struct MandatoryAlgorithmDispatch
+{
+ static constexpr guint mask = Mask;
+
+ template <class Visitor,
+ class Algorithm>
+ void
+ operator () (Visitor visitor,
+ const GimpPaintCoreLoopsParams *params,
+ GimpPaintCoreLoopsAlgorithm algorithms,
+ identity<Algorithm> algorithm) const
+ {
+ g_return_if_fail ((algorithms & Mask) == Mask);
+
+ BasicDispatch<AlgorithmTemplate, Mask, Dependencies...> () (visitor,
+ params,
+ algorithms,
+ algorithm);
+ }
+};
+
+/* SuppressedAlgorithmDispatch:
+ *
+ * A class template implementing a placeholder dispatch function suitable for
+ * dispatching algorithms that are never included in any hierarchy.
+ * 'AlgorithmTemplate' is the algorithm class template, 'Mask' is the dispatch
+ * function mask, as described in 'dispatch()', and 'Dependencies' is a list of
+ * (types of) dispatch functions the algorithm depends on. Note that
+ * 'AlgorithmTemplate' and 'Dependencies' are not actually used, and are merely
+ * included for exposition.
+ *
+ * 'SuppressedAlgorithmDispatch' verifies that the algorithm is not included in
+ * the set of requested algorithms (specifically, that the bitwise-AND of the
+ * requested-algorithms bitset and of 'Mask' is not equal to 'Mask'), and
+ * doesn't modify the hierarchy.
+ */
+
+template <template <class Base> class AlgorithmTemplate,
+ guint Mask,
+ class... Dependencies>
+struct SuppressedAlgorithmDispatch
+{
+ static constexpr guint mask = Mask;
+
+ template <class Visitor,
+ class Algorithm>
+ void
+ operator () (Visitor visitor,
+ const GimpPaintCoreLoopsParams *params,
+ GimpPaintCoreLoopsAlgorithm algorithms,
+ identity<Algorithm> algorithm) const
+ {
+ g_return_if_fail ((algorithms & Mask) != Mask);
+
+ visitor (algorithm);
+ }
+};
+
+
+/* PaintBuf, dispatch_paint_buf():
+ *
+ * An algorithm helper class, providing access to the paint buffer. Algorithms
+ * that use the paint buffer should specify 'dispatch_paint_buf()' as a
+ * dependency, and access 'PaintBuf' members through their base type/subobject.
+ */
+
+template <class Base>
+struct PaintBuf : Base
+{
+ /* Component type of the paint buffer. */
+ using paint_type = gfloat;
+
+ static constexpr guint filter = Base::filter | ALGORITHM_PAINT_BUF;
+
+ /* Paint buffer stride, in 'paint_type' elements. */
+ gint paint_stride;
+ /* Pointer to the start of the paint buffer data. */
+ paint_type *paint_data;
+
+ 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);
+ }
+};
+
+static BasicDispatch<PaintBuf, ALGORITHM_PAINT_BUF> dispatch_paint_buf;
+
+
+/* PaintMask, dispatch_paint_mask():
+ *
+ * An algorithm helper class, providing access to the paint mask. Algorithms
+ * that use the paint mask should specify 'dispatch_paint_mask()' as a
+ * dependency, and access 'PaintMask' members through their base type/
+ * subobject.
+ */
+
+template <class Base,
+ class MaskType>
+struct PaintMask : Base
+{
+ /* Component type of the paint mask. */
+ using mask_type = MaskType;
+
+ static constexpr guint filter = Base::filter | ALGORITHM_PAINT_MASK;
+
+ /* Paint mask stride, in 'mask_type' elements. */
+ gint mask_stride;
+ /* Pointer to the start of the paint mask data, taking the mask offset into
+ * account.
+ */
+ 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;
+
+
+/* Stipple, dispatch_stipple():
+ *
+ * An algorithm helper class, providing access to the 'stipple' parameter.
+ * Algorithms that use the 'stipple' parameter should specify
+ * 'dispatch_stipple()' as a dependency, and access 'Stipple' members through
+ * their base type/subobject.
+ */
+
+template <class Base,
+ gboolean StippleFlag>
+struct Stipple : Base
+{
+ static constexpr guint filter = Base::filter | ALGORITHM_STIPPLE;
+
+ /* The value of the 'stipple' parameter, usable as a constant expression. */
+ static constexpr gboolean stipple = StippleFlag;
+
+ 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;
+
+
+/* CompMask, dispatch_comp_mask(), has_comp_mask(), comp_mask_data():
+ *
+ * An algorithm helper class, providing access to the mask used for
+ * compositing. When this class is part of the hierarchy, 'DoLayerBlend' uses
+ * this buffer as the mask, instead of the input parameters' 'mask_buffer'.
+ * Algorithms that use the compositing mask should specify
+ * 'dispatch_comp_mask()' as a dependency, and access 'CompMask' members
+ * through their base type/subobject.
+ *
+ * Note that 'CompMask' only provides *access* to the compositing mask, but
+ * doesn't provide its actual *storage*. This is the responsibility of the
+ * algorithms that use 'CompMask'. Algorithms that need temporary storage for
+ * the compositing mask can use 'TempCompMask'.
+ *
+ * The 'has_comp_mask()' constexpr function determines if a given algorithm
+ * hierarchy uses the compositing mask.
+ *
+ * The 'comp_mask_data()' function returns a pointer to the compositing mask
+ * data for the current row if the hierarchy uses the compositing mask, or NULL
+ * otherwise.
+ */
+
+template <class Base>
+struct CompMask : Base
+{
+ /* Component type of the compositing mask. */
+ using comp_mask_type = gfloat;
+
+ static constexpr guint filter = Base::filter | ALGORITHM_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ /* Pointer to the compositing mask data for the current row. */
+ comp_mask_type *comp_mask_data;
+ };
+};
+
+static BasicDispatch<CompMask, ALGORITHM_COMP_MASK> dispatch_comp_mask;
+
+template <class Base>
+static constexpr gboolean
+has_comp_mask (const CompMask<Base> *algorithm)
+{
+ return TRUE;
+}
+
+static constexpr gboolean
+has_comp_mask (const AlgorithmBase *algorithm)
+{
+ return FALSE;
+}
+
+template <class Base,
+ class State>
+static gfloat *
+comp_mask_data (const CompMask<Base> *algorithm,
+ State *state)
+{
+ return state->comp_mask_data;
+}
+
+template <class State>
+static gfloat *
+comp_mask_data (const AlgorithmBase *algorithm,
+ State *state)
+{
+ return NULL;
+}
+
+
+/* TempCompMask, dispatch_temp_comp_mask():
+ *
+ * An algorithm helper class, providing temporary storage for the compositing
+ * mask. Algorithms that need a temporary compositing mask should specify
+ * 'dispatch_temp_comp_mask()' as a dependency, which itself includes
+ * 'dispatch_comp_mask()' as a dependency.
+ */
+
+template <class Base>
+struct TempCompMask : Base
+{
+ static constexpr guint filter = Base::filter | ALGORITHM_TEMP_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ using State = typename Base::template State<Derived>;
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->comp_mask_data = gegl_scratch_new (gfloat, rect->width);
+ }
+
+
+ template <class Derived>
+ void
+ finalize_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state) const
+ {
+ gegl_scratch_free (state->comp_mask_data);
+
+ Base::finalize_step (params, state);
+ }
+};
+
+static BasicDispatch<
+ TempCompMask,
+ ALGORITHM_TEMP_COMP_MASK,
+ decltype (dispatch_comp_mask)
+> dispatch_temp_comp_mask;
+
+
+/* CompBuffer, dispatch_comp_buffer(), has_comp_buffer(), comp_buffer_data():
+ *
+ * An algorithm helper class, providing access to the output buffer used for
+ * compositing. When this class is part of the hierarchy, 'DoLayerBlend' uses
+ * this buffer as the output buffer, instead of the input parameters'
+ * 'dest_buffer'. Algorithms that use the compositing buffer should specify
+ * 'dispatch_comp_buffer()' as a dependency, and access 'CompBuffer' members
+ * through their base type/subobject.
+ *
+ * Note that 'CompBuffer' only provides *access* to the compositing buffer, but
+ * doesn't provide its actual *storage*. This is the responsibility of the
+ * algorithms that use 'CompBuffer'. Algorithms that need temporary storage
+ * for the compositing buffer can use 'TempCompBuffer'.
+ *
+ * The 'has_comp_buffer()' constexpr function determines if a given algorithm
+ * hierarchy uses the compositing buffer.
+ *
+ * The 'comp_buffer_data()' function returns a pointer to the compositing
+ * buffer data for the current row if the hierarchy uses the compositing
+ * buffer, or NULL otherwise.
+ */
+
+template <class Base>
+struct CompBuffer : Base
+{
+ /* Component type of the compositing buffer. */
+ using comp_buffer_type = gfloat;
+
+ static constexpr guint filter = Base::filter | ALGORITHM_COMP_BUFFER;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ /* Pointer to the compositing buffer data for the current row. */
+ comp_buffer_type *comp_buffer_data;
+ };
+};
+
+static BasicDispatch<CompBuffer, ALGORITHM_COMP_BUFFER> dispatch_comp_buffer;
+
+template <class Base>
+static constexpr gboolean
+has_comp_buffer (const CompBuffer<Base> *algorithm)
+{
+ return TRUE;
+}
+
+static constexpr gboolean
+has_comp_buffer (const AlgorithmBase *algorithm)
+{
+ return FALSE;
+}
+
+template <class Base,
+ class State>
+static gfloat *
+comp_buffer_data (const CompBuffer<Base> *algorithm,
+ State *state)
+{
+ return state->comp_buffer_data;
+}
+
+template <class State>
+static gfloat *
+comp_buffer_data (const AlgorithmBase *algorithm,
+ State *state)
+{
+ return NULL;
+}
+
+
+/* TempCompBuffer, dispatch_temp_comp_buffer():
+ *
+ * An algorithm helper class, providing temporary storage for the compositing
+ * buffer. Algorithms that need a temporary compositing buffer should specify
+ * 'dispatch_temp_comp_buffer()' as a dependency, which itself includes
+ * 'dispatch_comp_buffer()' as a dependency.
+ */
+
+template <class Base>
+struct TempCompBuffer : Base
+{
+ static constexpr guint filter = Base::filter | ALGORITHM_TEMP_COMP_BUFFER;
+
+ using Base::Base;
+
+ template <class Derived>
+ using State = typename Base::template State<Derived>;
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->comp_buffer_data = gegl_scratch_new (gfloat, 4 * rect->width);
+ }
+
+
+ template <class Derived>
+ void
+ finalize_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state) const
+ {
+ gegl_scratch_free (state->comp_buffer_data);
+
+ Base::finalize_step (params, state);
+ }
+};
+
+static BasicDispatch<
+ TempCompBuffer,
+ ALGORITHM_TEMP_COMP_BUFFER,
+ decltype (dispatch_comp_buffer)
+> dispatch_temp_comp_buffer;
+
+
+/* CanvasBufferIterator, DispatchCanvasBufferIterator:
+ *
+ * An algorithm helper class, providing iterator-access to the canvas buffer.
+ * Algorithms that iterate over the canvas buffer should specify
+ * 'DispatchCanvasBufferIterator<Access>' as a dependency, where 'Access' is
+ * the desired access mode to the canvas buffer, and access its members through
+ * their base type/subobject.
+ */
+
+template <class Base,
+ guint Access,
+ gboolean First>
+struct CanvasBufferIterator;
+
+template <class Base,
+ guint Access>
+static constexpr gboolean
+canvas_buffer_iterator_is_first (CanvasBufferIterator<Base, Access, TRUE> *algorithm)
+{
+ return FALSE;
+}
+
+static constexpr gboolean
+canvas_buffer_iterator_is_first (AlgorithmBase *algorithm)
+{
+ return TRUE;
+}
+
+template <class Base,
+ guint Access,
+ gboolean First = canvas_buffer_iterator_is_first ((Base *) NULL)>
+struct CanvasBufferIterator : Base
+{
+ /* The combined canvas-buffer access mode used by the hierarchy, up to, and
+ * including, the current class.
+ */
+ static constexpr GeglAccessMode canvas_buffer_access =
+ (GeglAccessMode) (Base::canvas_buffer_access | Access);
+
+ using Base::Base;
+};
+
+template <class Base,
+ guint Access>
+struct CanvasBufferIterator<Base, Access, TRUE> : Base
+{
+ /* The combined canvas-buffer access mode used by the hierarchy, up to, and
+ * including, the current class.
+ */
+ static constexpr GeglAccessMode canvas_buffer_access =
+ (GeglAccessMode) Access;
+
+ static constexpr gint max_n_iterators =
+ Base::max_n_iterators + 1;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ gint canvas_buffer_iterator;
+ };
+
+ template <class Derived>
+ void
+ init (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area) const
+ {
+ state->canvas_buffer_iterator = gegl_buffer_iterator_add (
+ iter, params->canvas_buffer, area, 0, babl_format ("Y float"),
+ Derived::canvas_buffer_access, GEGL_ABYSS_NONE);
+
+ /* initialize the base class *after* initializing the iterator, to make
+ * sure that canvas_buffer is the primary buffer of the iterator, if no
+ * subclass added an iterator first.
+ */
+ Base::init (params, state, iter, roi, area);
+ }
+};
+
+template <guint Access>
+struct DispatchCanvasBufferIteratorHelper
+{
+ template <class Base>
+ using algorithm_template = CanvasBufferIterator<Base, Access>;
+};
+
+template <guint Access>
+using DispatchCanvasBufferIterator = BasicDispatch<
+ DispatchCanvasBufferIteratorHelper<Access>::template algorithm_template,
+ ALGORITHM_CANVAS_BUFFER_ITERATOR
+>;
+
+
+/* MaskBufferIterator, mask_buffer_iterator_dispatch(),
+ * has_mask_buffer_iterator():
+ *
+ * An algorithm helper class, providing read-only iterator-access to the mask
+ * buffer. Algorithms that iterate over the mask buffer should specify
+ * 'dispatch_mask_buffer_iterator()' as a dependency, and access
+ * 'MaskBufferIterator' members through their base type/subobject.
+ *
+ * The 'has_mask_buffer_iterator()' constexpr function determines if a given
+ * algorithm hierarchy uses has a mask-buffer iterator.
+ *
+ * The 'mask_buffer_iterator()' function returns the index of the mask-buffer
+ * iterator if the hierarchy has one, or -1 otherwise.
+ */
+
+template <class Base>
+struct MaskBufferIterator : Base
+{
+ static constexpr guint filter = Base::filter | ALGORITHM_MASK_BUFFER_ITERATOR;
+
+ static constexpr gint max_n_iterators = Base::max_n_iterators + 1;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ gint mask_buffer_iterator;
+ };
+
+ 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;
+
+ state->mask_buffer_iterator = gegl_buffer_iterator_add (
+ iter, params->mask_buffer, &mask_area, 0, babl_format ("Y float"),
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ }
+};
+
+struct DispatchMaskBufferIterator
+{
+ static constexpr guint mask = ALGORITHM_MASK_BUFFER_ITERATOR;
+
+ template <class Visitor,
+ class Algorithm>
+ void
+ operator () (Visitor visitor,
+ const GimpPaintCoreLoopsParams *params,
+ GimpPaintCoreLoopsAlgorithm algorithms,
+ identity<Algorithm> algorithm) const
+ {
+ if (params->mask_buffer)
+ visitor (identity<MaskBufferIterator<Algorithm>> ());
+ else
+ visitor (algorithm);
+ }
+} static dispatch_mask_buffer_iterator;
+
+template <class Base>
+static constexpr gboolean
+has_mask_buffer_iterator (const MaskBufferIterator<Base> *algorithm)
+{
+ return TRUE;
+}
+
+static constexpr gboolean
+has_mask_buffer_iterator (const AlgorithmBase *algorithm)
+{
+ return FALSE;
+}
+
+template <class Base,
+ class State>
+static gint
+mask_buffer_iterator (const MaskBufferIterator<Base> *algorithm,
+ State *state)
+{
+ return state->mask_buffer_iterator;
+}
+
+template <class State>
+static gint
+mask_buffer_iterator (const AlgorithmBase *algorithm,
+ State *state)
+{
+ return -1;
+}
+
+
+/* CombinePaintMaskToCanvasBufferToPaintBufAlpha,
+ * dispatch_combine_paint_mask_to_canvas_buffer_to_paint_buf_alpha():
+ *
+ * An algorithm class, providing an optimized version combining both the
+ * COMBINE_PAINT_MASK_TO_CANVAS_BUFFER and the CANVAS_BUFFER_TO_PAINT_BUF_ALPHA
+ * algorithms. Used instead of the individual implementations, when both
+ * algorithms are requested.
+ */
+
+template <class Base>
+struct CombinePaintMaskToCanvasBufferToPaintBufAlpha : Base
+{
+ using mask_type = typename Base::mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::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 GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->canvas_pixel =
+ (gfloat *) iter->items[state->canvas_buffer_iterator].data;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gint mask_offset = (y - roi->y) * this->mask_stride +
+ (rect->x - roi->x);
+ const mask_type *mask_pixel = &this->mask_data[mask_offset];
+ gint paint_offset = (y - roi->y) * this->paint_stride +
+ (rect->x - roi->x) * 4;
+ gfloat *paint_pixel = &this->paint_data[paint_offset];
+ gint x;
+
+ for (x = 0; x < rect->width; x++)
+ {
+ if (Base::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 SuppressedAlgorithmDispatch<
+ CombinePaintMaskToCanvasBufferToPaintBufAlpha,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA,
+ decltype (dispatch_paint_buf),
+ decltype (dispatch_paint_mask),
+ decltype (dispatch_stipple),
+ DispatchCanvasBufferIterator<GEGL_BUFFER_READWRITE>
+>
+dispatch_combine_paint_mask_to_canvas_buffer_to_paint_buf_alpha;
+
+
+/* CombinePaintMaskToCanvasBuffer,
+ * dispatch_combine_paint_mask_to_canvas_buffer():
+ *
+ * An algorithm class, implementing the COMBINE_PAINT_MASK_TO_CANVAS_BUFFER
+ * algorithm.
+ */
+
+template <class Base>
+struct CombinePaintMaskToCanvasBuffer : Base
+{
+ using mask_type = typename Base::mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::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 GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->canvas_pixel =
+ (gfloat *) iter->items[state->canvas_buffer_iterator].data;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gint mask_offset = (y - roi->y) * this->mask_stride +
+ (rect->x - roi->x);
+ const mask_type *mask_pixel = &this->mask_data[mask_offset];
+ gint x;
+
+ for (x = 0; x < rect->width; x++)
+ {
+ if (Base::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<
+ CombinePaintMaskToCanvasBuffer,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER,
+ decltype (dispatch_paint_mask),
+ decltype (dispatch_stipple),
+ DispatchCanvasBufferIterator<GEGL_BUFFER_READWRITE>
+>
+dispatch_combine_paint_mask_to_canvas_buffer;
+
+
+/* CanvasBufferToPaintBufAlpha, dispatch_canvas_buffer_to_paint_buf_alpha():
+ *
+ * An algorithm class, implementing the CANVAS_BUFFER_TO_PAINT_BUF_ALPHA
+ * algorithm.
+ */
+
+template <class Base>
+struct CanvasBufferToPaintBufAlpha : Base
+{
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::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 GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->canvas_pixel =
+ (const gfloat *) iter->items[state->canvas_buffer_iterator].data;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ /* Copy the canvas buffer in rect to the paint buffer's alpha channel */
+
+ gint paint_offset = (y - roi->y) * this->paint_stride +
+ (rect->x - roi->x) * 4;
+ gfloat *paint_pixel = &this->paint_data[paint_offset];
+ gint x;
+
+ for (x = 0; x < rect->width; x++)
+ {
+ paint_pixel[3] *= *state->canvas_pixel;
+
+ state->canvas_pixel += 1;
+ paint_pixel += 4;
+ }
+ }
+};
+
+static SuppressedAlgorithmDispatch<
+ CanvasBufferToPaintBufAlpha,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA,
+ decltype (dispatch_paint_buf),
+ DispatchCanvasBufferIterator<GEGL_BUFFER_READ>
+>
+dispatch_canvas_buffer_to_paint_buf_alpha;
+
+
+/* PaintMaskToPaintBufAlpha, dispatch_paint_mask_to_paint_buf_alpha():
+ *
+ * An algorithm class, implementing the PAINT_MASK_TO_PAINT_BUF_ALPHA
+ * algorithm.
+ */
+
+template <class Base>
+struct PaintMaskToPaintBufAlpha : Base
+{
+ using mask_type = typename Base::mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ explicit
+ PaintMaskToPaintBufAlpha (const GimpPaintCoreLoopsParams *params) :
+ Base (params)
+ {
+ /* Validate that the paint buffer is within 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,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gint paint_offset = (y - roi->y) * this->paint_stride +
+ (rect->x - roi->x) * 4;
+ gfloat *paint_pixel = &this->paint_data[paint_offset];
+ gint mask_offset = (y - roi->y) * this->mask_stride +
+ (rect->x - roi->x);
+ const mask_type *mask_pixel = &this->mask_data[mask_offset];
+ gint x;
+
+ for (x = 0; x < rect->width; x++)
+ {
+ paint_pixel[3] *= value_to_float (*mask_pixel) * params->paint_opacity;
+
+ mask_pixel += 1;
+ paint_pixel += 4;
+ }
+ }
+};
+
+static SuppressedAlgorithmDispatch<
+ PaintMaskToPaintBufAlpha,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA,
+ decltype (dispatch_paint_buf),
+ decltype (dispatch_paint_mask)
+>
+dispatch_paint_mask_to_paint_buf_alpha;
+
+
+/* CanvasBufferToCompMask, dispatch_canvas_buffer_to_comp_mask():
+ *
+ * An algorithm class, implementing the CANVAS_BUFFER_TO_COMP_MASK algorithm.
+ */
+
+template <class Base,
+ gboolean Direct>
+struct CanvasBufferToCompMask : Base
+{
+ using comp_mask_type = typename Base::comp_mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ const gfloat *canvas_pixel;
+ const gfloat *mask_pixel;
+ };
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->canvas_pixel =
+ (const gfloat *) iter->items[state->canvas_buffer_iterator].data;
+ state->mask_pixel =
+ (const gfloat *) iter->items[state->mask_buffer_iterator].data;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ comp_mask_type *comp_mask_pixel = state->comp_mask_data;
+ gint x;
+
+ for (x = 0; x < rect->width; x++)
+ {
+ comp_mask_pixel[0] = state->canvas_pixel[0] * state->mask_pixel[0];
+
+ comp_mask_pixel += 1;
+ state->canvas_pixel += 1;
+ state->mask_pixel += 1;
+ }
+ }
+};
+
+template <class Base>
+struct CanvasBufferToCompMask<Base, TRUE> : Base
+{
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ using State = typename Base::template State<Derived>;
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->comp_mask_data =
+ (gfloat *) iter->items[state->canvas_buffer_iterator].data - rect->width;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ state->comp_mask_data += rect->width;
+ }
+};
+
+struct DispatchCanvasBufferToCompMask
+{
+ static constexpr guint mask =
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK;
+
+ template <class Base>
+ using AlgorithmDirect = CanvasBufferToCompMask<Base, TRUE>;
+ template <class Base>
+ using AlgorithmIndirect = CanvasBufferToCompMask<Base, FALSE>;
+
+ using DispatchDirect = BasicDispatch<
+ AlgorithmDirect,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK,
+ decltype (dispatch_comp_mask)
+ >;
+ using DispatchIndirect = BasicDispatch<
+ AlgorithmIndirect,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK,
+ decltype (dispatch_temp_comp_mask)
+ >;
+
+ template <class Algorithm,
+ gboolean HasMaskBufferIterator = has_mask_buffer_iterator (
+ (Algorithm *) NULL)>
+ struct Dispatch : DispatchIndirect
+ {
+ };
+
+ template <class Algorithm>
+ struct Dispatch<Algorithm, FALSE> : DispatchDirect
+ {
+ };
+
+ 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;
+
+ Dispatch<NewAlgorithm> () (visitor, params, algorithms, algorithm);
+ },
+ params, algorithms, algorithm,
+ DispatchCanvasBufferIterator<GEGL_BUFFER_READ> (),
+ dispatch_mask_buffer_iterator);
+ }
+ else
+ {
+ visitor (algorithm);
+ }
+ }
+} static dispatch_canvas_buffer_to_comp_mask;
+
+
+/* PaintMaskToCompMask, dispatch_paint_mask_to_comp_mask():
+ *
+ * An algorithm class, implementing the PAINT_MASK_TO_COMP_MASK algorithm.
+ */
+
+template <class Base,
+ gboolean Direct>
+struct PaintMaskToCompMask : Base
+{
+ using mask_type = typename Base::mask_type;
+ using comp_mask_type = typename Base::comp_mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ const gfloat *mask_pixel;
+ };
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ if (has_mask_buffer_iterator (this))
+ {
+ state->mask_pixel =
+ (const gfloat *) iter->items[mask_buffer_iterator (this, state)].data;
+ }
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gint mask_offset = (y - roi->y) * this->mask_stride +
+ (rect->x - roi->x);
+ const mask_type *mask_pixel = &this->mask_data[mask_offset];
+ comp_mask_type *comp_mask_pixel = state->comp_mask_data;
+ gint x;
+
+ if (has_mask_buffer_iterator (this))
+ {
+ for (x = 0; x < rect->width; x++)
+ {
+ comp_mask_pixel[0] = value_to_float (mask_pixel[0]) *
+ state->mask_pixel[0] *
+ params->paint_opacity;
+
+ comp_mask_pixel += 1;
+ mask_pixel += 1;
+ state->mask_pixel += 1;
+ }
+ }
+ else
+ {
+ for (x = 0; x < rect->width; x++)
+ {
+ comp_mask_pixel[0] = value_to_float (mask_pixel[0]) *
+ params->paint_opacity;
+
+ comp_mask_pixel += 1;
+ mask_pixel += 1;
+ }
+ }
+ }
+};
+
+template <class Base>
+struct PaintMaskToCompMask<Base, TRUE> : Base
+{
+ using mask_type = typename Base::mask_type;
+
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK;
+
+ using Base::Base;
+
+ template <class Derived>
+ using State = typename Base::template State<Derived>;
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ gint mask_offset = (rect->y - roi->y) * this->mask_stride +
+ (rect->x - roi->x);
+
+ state->comp_mask_data = (mask_type *) &this->mask_data[mask_offset] -
+ this->mask_stride;
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ state->comp_mask_data += this->mask_stride;
+ }
+};
+
+struct DispatchPaintMaskToCompMask
+{
+ static constexpr guint mask =
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
+
+ template <class Base>
+ using AlgorithmDirect = PaintMaskToCompMask<Base, TRUE>;
+ template <class Base>
+ using AlgorithmIndirect = PaintMaskToCompMask<Base, FALSE>;
+
+ using DispatchDirect = BasicDispatch<
+ AlgorithmDirect,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK,
+ decltype (dispatch_comp_mask)
+ >;
+ using DispatchIndirect = BasicDispatch<
+ AlgorithmIndirect,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK,
+ decltype (dispatch_temp_comp_mask)
+ >;
+
+ template <class Algorithm,
+ class MaskType = typename Algorithm::mask_type,
+ gboolean HasMaskBufferIterator = has_mask_buffer_iterator (
+ (Algorithm *) NULL)>
+ struct Dispatch : DispatchIndirect
+ {
+ };
+
+ template <class Algorithm>
+ struct Dispatch<Algorithm, gfloat, FALSE> : DispatchDirect
+ {
+ };
+
+ 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;
+
+ if (params->paint_opacity == GIMP_OPACITY_OPAQUE)
+ Dispatch<NewAlgorithm> () (visitor, params, algorithms, algorithm);
+ else
+ DispatchIndirect () (visitor, params, algorithms, algorithm);
+ },
+ params, algorithms, algorithm,
+ dispatch_paint_mask,
+ dispatch_mask_buffer_iterator);
+ }
+ else
+ {
+ visitor (algorithm);
+ }
+ }
+} static dispatch_paint_mask_to_comp_mask;
+
+
+/* DoLayerBlend, dispatch_do_layer_blend():
+ *
+ * An algorithm class, implementing the DO_LAYER_BLEND algorithm.
+ */
+
+template <class Base>
+struct DoLayerBlend : Base
+{
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND;
+
+ static constexpr gint max_n_iterators = Base::max_n_iterators + 2;
+
+ 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);
+
+ iterator_format = gimp_layer_mode_get_format (params->paint_mode,
+ layer_mode.blend_space,
+ layer_mode.composite_space,
+ layer_mode.composite_mode,
+ 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>
+ {
+ gint iterator_base;
+
+ 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
+ {
+ state->iterator_base = gegl_buffer_iterator_add (iter, params->src_buffer,
+ area, 0, iterator_format,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE);
+
+ if (! has_comp_buffer ((const Derived *) this))
+ {
+ gegl_buffer_iterator_add (iter, params->dest_buffer, area, 0,
+ iterator_format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+ }
+
+ /* initialize the base class *after* initializing the iterator, to make
+ * sure that src_buffer is the primary buffer of the iterator, if no
+ * subclass added an iterator first.
+ */
+ Base::init (params, state, iter, roi, area);
+ }
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->in_pixel = (gfloat *) iter->items[state->iterator_base + 0].data;
+
+ state->paint_pixel = this->paint_data +
+ (rect->y - roi->y) * this->paint_stride +
+ (rect->x - roi->x) * 4;
+
+ if (! has_comp_mask (this) && has_mask_buffer_iterator (this))
+ {
+ state->mask_pixel =
+ (gfloat *) iter->items[mask_buffer_iterator (this, state)].data;
+ }
+
+ if (! has_comp_buffer ((const Derived *) this))
+ state->out_pixel = (gfloat *) iter->items[state->iterator_base + 1].data;
+
+ state->process_roi.x = rect->x;
+ state->process_roi.width = rect->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,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gfloat *mask_pixel;
+ gfloat *out_pixel;
+
+ if (has_comp_mask (this))
+ mask_pixel = comp_mask_data (this, state);
+ else if (has_mask_buffer_iterator (this))
+ mask_pixel = state->mask_pixel;
+ else
+ mask_pixel = NULL;
+
+ if (! has_comp_buffer ((const Derived *) this))
+ {
+ out_pixel = state->out_pixel;
+ }
+ else
+ {
+ out_pixel = comp_buffer_data (
+ (const Derived *) this,
+ (typename Derived::template State<Derived> *) state);
+ }
+
+ state->process_roi.y = y;
+
+ layer_mode.function ((GeglOperation*) &layer_mode,
+ state->in_pixel,
+ state->paint_pixel,
+ mask_pixel,
+ out_pixel,
+ rect->width,
+ &state->process_roi,
+ 0);
+
+ state->in_pixel += rect->width * 4;
+ state->paint_pixel += this->paint_stride;
+ if (! has_comp_mask (this) && has_mask_buffer_iterator (this))
+ state->mask_pixel += rect->width;
+ if (! has_comp_buffer ((const Derived *) this))
+ state->out_pixel += rect->width * 4;
+ }
+};
+
+static MandatoryAlgorithmDispatch<
+ DoLayerBlend,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND,
+ decltype (dispatch_paint_buf),
+ decltype (dispatch_mask_buffer_iterator)
+>
+dispatch_do_layer_blend;
+
+
+/* MaskComponents, dispatch_mask_components():
+ *
+ * An algorithm class, implementing the MASK_COMPONENTS algorithm.
+ */
+
+template <class Base>
+struct MaskComponents : Base
+{
+ static constexpr guint filter =
+ Base::filter |
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_MASK_COMPONENTS;
+
+ static constexpr gint max_n_iterators = Base::max_n_iterators + 1;
+
+ const Babl *format;
+ const Babl *comp_fish = NULL;
+
+ explicit
+ MaskComponents (const GimpPaintCoreLoopsParams *params) :
+ Base (params)
+ {
+ format = gimp_operation_mask_components_get_format (
+ gegl_buffer_get_format (params->dest_buffer));
+
+ if (format != this->iterator_format)
+ comp_fish = babl_fish (this->iterator_format, format);
+ }
+
+ template <class Derived>
+ struct State : Base::template State<Derived>
+ {
+ gint dest_buffer_iterator;
+
+ guint8 *dest_pixel;
+ guint8 *comp_pixel;
+ };
+
+ template <class Derived>
+ void
+ init (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area) const
+ {
+ state->dest_buffer_iterator = gegl_buffer_iterator_add (
+ iter, params->dest_buffer, area, 0, format,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+
+ /* initialize the base class *after* initializing the iterator, to make
+ * sure that dest_buffer is the primary buffer of the iterator, if no
+ * subclass added an iterator first.
+ */
+ Base::init (params, state, iter, roi, area);
+ }
+
+ template <class Derived>
+ void
+ init_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect) const
+ {
+ Base::init_step (params, state, iter, roi, area, rect);
+
+ state->dest_pixel =
+ (guint8 *) iter->items[state->dest_buffer_iterator].data;
+
+ if (comp_fish)
+ {
+ state->comp_pixel = (guint8 *) gegl_scratch_alloc (
+ rect->width * babl_format_get_bytes_per_pixel (format));
+ }
+ }
+
+ template <class Derived>
+ void
+ process_row (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state,
+ GeglBufferIterator *iter,
+ const GeglRectangle *roi,
+ const GeglRectangle *area,
+ const GeglRectangle *rect,
+ gint y) const
+ {
+ Base::process_row (params, state, iter, roi, area, rect, y);
+
+ gpointer comp_pixel;
+
+ if (comp_fish)
+ {
+ babl_process (comp_fish,
+ state->comp_buffer_data, state->comp_pixel,
+ rect->width);
+
+ comp_pixel = state->comp_pixel;
+ }
+ else
+ {
+ comp_pixel = state->comp_buffer_data;
+ }
+
+ gimp_operation_mask_components_process (format,
+ state->dest_pixel, comp_pixel,
+ state->dest_pixel,
+ rect->width, params->affect);
+
+ state->dest_pixel += rect->width * babl_format_get_bytes_per_pixel (format);
+ }
+
+ template <class Derived>
+ void
+ finalize_step (const GimpPaintCoreLoopsParams *params,
+ State<Derived> *state) const
+ {
+ if (comp_fish)
+ gegl_scratch_free (state->comp_pixel);
+
+ Base::finalize_step (params, state);
+ }
+};
+
+static AlgorithmDispatch<
+ MaskComponents,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_MASK_COMPONENTS,
+ decltype (dispatch_temp_comp_buffer)
+>
+dispatch_mask_components;
+
+
+/* gimp_paint_core_loops_process():
+ *
+ * Performs the set of algorithms requested in 'algorithms', specified as a
+ * bitwise-OR of 'GimpPaintCoreLoopsAlgorithm' values, given the set of
+ * parameters 'params'.
+ *
+ * Note that the order in which the algorithms are performed is currently
+ * fixed, and follows their order of appearance in the
+ * 'GimpPaintCoreLoopsAlgorithm' enum.
+ */
+
+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);
+
+ gegl_parallel_distribute_area (
+ &roi, PIXELS_PER_THREAD,
+ [=] (const GeglRectangle *area)
+ {
+ State state;
+ gint y;
+
+ if (Algorithm::max_n_iterators > 0)
+ {
+ GeglBufferIterator *iter;
+
+ iter = gegl_buffer_iterator_empty_new (
+ Algorithm::max_n_iterators);
+
+ algorithm.init (params, &state, iter, &roi, area);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const GeglRectangle *rect = &iter->items[0].roi;
+
+ algorithm.init_step (params, &state, iter, &roi, area, rect);
+
+ for (y = 0; y < rect->height; y++)
+ {
+ algorithm.process_row (params, &state,
+ iter, &roi, area, rect,
+ rect->y + y);
+ }
+
+ algorithm.finalize_step (params, &state);
+ }
+
+ algorithm.finalize (params, &state);
+ }
+ else
+ {
+ algorithm.init (params, &state, NULL, &roi, area);
+ algorithm.init_step (params, &state, NULL, &roi, area, area);
+
+ for (y = 0; y < area->height; y++)
+ {
+ algorithm.process_row (params, &state,
+ NULL, &roi, area, area,
+ area->y + y);
+ }
+
+ algorithm.finalize_step (params, &state);
+ algorithm.finalize (params, &state);
+ }
+ });
+ },
+ params, algorithms, identity<AlgorithmBase> (),
+ dispatch_combine_paint_mask_to_canvas_buffer_to_paint_buf_alpha,
+ dispatch_combine_paint_mask_to_canvas_buffer,
+ dispatch_canvas_buffer_to_paint_buf_alpha,
+ dispatch_paint_mask_to_paint_buf_alpha,
+ dispatch_canvas_buffer_to_comp_mask,
+ dispatch_paint_mask_to_comp_mask,
+ dispatch_do_layer_blend,
+ dispatch_mask_components);
+}