[gimp] app: refactor gimppaintcore-loops to coalesce iteration
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: refactor gimppaintcore-loops to coalesce iteration
- Date: Thu, 19 Apr 2018 09:37:50 +0000 (UTC)
commit f2a1fd5bf0e170e79bc3c2f1f162d2f73ecb9747
Author: Ell <ell_se yahoo com>
Date: Sat Apr 14 19:02:21 2018 -0400
app: refactor gimppaintcore-loops to coalesce iteration
The gimppaintcore-loops functions perform very little actual
computational work (in case of do_layer_blend(), at least for
simple blend modes), which makes the cost of buffer iteration, and
memory bandwidth, nonnegligible factors. Since these functions are
usually called in succession, acessing the same region of the same
buffers, using the same foramts, coalescing them into a single
function, which performs all the necessary processing in a single
step, can improve performance when these functions are the
bottleneck.
Add a gimp_paint_core_loops_process() function, which does just
that: it takes a set of algorithms to run, and a set of parameters,
and performs all of them in one go. The individual functions are
kept for convenience, but are merely wrappers around
gimp_paint_core_loops_process().
Be warned: the implementation uses unholy C++ from outer space, in
order to make this (sort of) managable. See the comments for more
details.
app/paint/gimppaintcore-loops.cc | 1494 ++++++++++++++++++++++++++++++--------
app/paint/gimppaintcore-loops.h | 116 ++-
app/paint/gimppaintcore.c | 75 +-
3 files changed, 1315 insertions(+), 370 deletions(-)
---
diff --git a/app/paint/gimppaintcore-loops.cc b/app/paint/gimppaintcore-loops.cc
index c02335d..9b1fdee 100644
--- a/app/paint/gimppaintcore-loops.cc
+++ b/app/paint/gimppaintcore-loops.cc
@@ -34,212 +34,1211 @@ 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)
+/* 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
{
- 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)
+/* 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;
+
+ /* See CanvasBufferIterator. */
+ static constexpr gint canvas_buffer_iterator = -1;
+ static constexpr GeglAccessMode canvas_buffer_access = {};
+
+ /* The current number of iterators used by the hierarchy. Algorithms should
+ * use the 'n_iterators' value of their base class as the base-index for
+ * their iterators, and redefine 'n_iterators' by adding the number of
+ * iterators they use to this value.
+ */
+ static constexpr gint 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. '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()'.
+ *
+ * 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
+ {
+ }
+
+ /* 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()', 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,
+ gint y) const
+ {
+ }
+};
+
+
+/* BasicDispatch:
+ *
+ * A class template implementing a simple dispatch function object, which adds
+ * an algorithm to the heirarchy unconditionally. 'AlgorithmTemplate' is the
+ * alogithm class template (usually a helper class, rather than an actual
+ * algorithm), and 'Mask' is the dispatch function mask, as described in
+ * 'dispatch()'.
+ */
+
+template <template <class Base> class AlgorithmTemplate,
+ 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<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, usd as
+ * explained below.
+ *
+ * 'AlgorithmDispatch' adds the algorithm to the heirarchy 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);
+ }
+ }
+};
+
+
+/* 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;
+
+
+/* CanvasBufferIterator:
+ *
+ * An algorithm helper class, providing iterator-access to the canvas buffer.
+ * Algorithms that iterate over the canvas buffer should derive from this
+ * class, and access its members through their base type/subobject.
+ *
+ * 'Base' is the base class to use, which should normally be the base template-
+ * parameter class passed to the algorithm. 'Access' specifies the desired
+ * iterator access to the canvas buffer.
+ */
+
+template <class Base,
+ guint Access>
+struct CanvasBufferIterator : Base
+{
+ /* The iterator index of the canvas buffer. */
+ static constexpr gint canvas_buffer_iterator =
+ Base::canvas_buffer_iterator < 0 ? Base::n_iterators :
+ Base::canvas_buffer_iterator;
+ /* Used internally. */
+ static constexpr GeglAccessMode canvas_buffer_access =
+ (GeglAccessMode) (Base::canvas_buffer_access | Access);
+ /* The total number of iterators used by the hierarchy, up to, and including,
+ * the current class.
+ */
+ 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);
+ }
+ }
+};
+
+
+/* CombinePaintMaskToCanvasMaskToPaintBufAlpha,
+ * dispatch_combine_paint_mask_to_canvas_mask_to_paint_buf_alpha():
+ *
+ * An algorithm class, providing an optimized version combining both the
+ * COMBINE_PAINT_MASK_TO_CANVAS_MASK and the CANVAS_BUFFER_TO_PAINT_BUF_ALPHA
+ * algorithms. Used instead of the individual implementations, when both
+ * algorithms are requested.
+ */
+
+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 - 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 - 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;
+
+
+/* CombinePaintMaskToCanvasMask, dispatch_combine_paint_mask_to_canvas_mask():
+ *
+ * An algorithm class, implementing the COMBINE_PAINT_MASK_TO_CANVAS_MASK
+ * algorithm.
+ */
+
+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 - 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;
+
+
+/* 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 : 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 - 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;
+
+
+/* PaintMaskToPaintBuffer, dispatch_paint_mask_to_paint_buffer():
+ *
+ * An algorithm class, implementing the PAINT_MASK_TO_PAINT_BUFFER algorithm.
+ */
+
+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 - roi->y) * this->paint_stride +
+ (iter->roi[0].x - roi->x) * 4;
+ gfloat *paint_pixel = &this->paint_data[paint_offset];
+ gint mask_offset = (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;
+
+
+/* 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 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 = 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;
+
+
+/* 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)
{
- GeglBufferIterator *iter;
+ 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>;
- iter = gegl_buffer_iterator_new (canvas_buffer, area, 0,
- babl_format ("Y float"),
- GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+ Algorithm algorithm (params);
- if (stipple)
+ gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
+ [=] (const GeglRectangle *area)
{
- if (mask_format == babl_format ("Y u8"))
+ State state;
+ gint y;
+
+ if (Algorithm::n_iterators > 0)
{
- const guint8 *mask_data = (const guint8 *) 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 guint8 *mask_pixel = &mask_data[mask_offset];
-
- for (ix = 0; ix < iter->roi[0].width; ix++)
- {
- out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel / 255.0f) * opacity;
-
- mask_pixel += 1;
- out_pixel += 1;
- }
+ algorithm.process_row (params, &state,
+ iter, &roi, area,
+ iter->roi[0].y + y);
}
}
}
- else if (mask_format == babl_format ("Y float"))
+ else
{
- 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;
+ GeglBufferIterator iter;
- 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];
+ iter.roi[0] = *area;
- for (ix = 0; ix < iter->roi[0].width; ix++)
- {
- out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel) * opacity;
+ algorithm.init (params, &state, &iter, &roi, area);
+ algorithm.init_step (params, &state, &iter, &roi, area);
- mask_pixel += 1;
- out_pixel += 1;
- }
- }
+ for (y = 0; y < iter.roi[0].height; y++)
+ {
+ algorithm.process_row (params, &state,
+ &iter, &roi, area,
+ iter.roi[0].y + y);
}
}
- 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;
+ });
+ },
+ 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);
+}
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *out_pixel = (gfloat *)iter->data[0];
- int iy, ix;
- 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];
+/* combine_paint_mask_to_canvas_mask():
+ *
+ * A convenience wrapper around 'gimp_paint_core_loops_process()', performing
+ * just the COMBINE_PAINT_MASK_TO_CANVAS_MASK algorithm.
+ */
- 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;
+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)
+{
+ GimpPaintCoreLoopsParams params = {};
- 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;
+ params.canvas_buffer = canvas_buffer;
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *out_pixel = (gfloat *)iter->data[0];
- int iy, ix;
+ params.paint_buf_offset_x = x_offset;
+ params.paint_buf_offset_y = y_offset;
- 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];
+ params.paint_mask = paint_mask;
+ params.paint_mask_offset_x = mask_x_offset;
+ params.paint_mask_offset_y = mask_y_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;
+ params.stipple = stipple;
- mask_pixel += 1;
- out_pixel += 1;
- }
- }
- }
- }
- else
- {
- g_warning("Mask format not supported: %s", babl_get_name (mask_format));
- }
- }
- });
+ params.paint_opacity = opacity;
+
+ gimp_paint_core_loops_process (
+ ¶ms,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK);
}
+
+/* canvas_buffer_to_paint_buf_alpha():
+ *
+ * A convenience wrapper around 'gimp_paint_core_loops_process()', performing
+ * just the CANVAS_BUFFER_TO_PAINT_BUF_ALPHA algorithm.
+ */
+
void
canvas_buffer_to_paint_buf_alpha (GimpTempBuf *paint_buf,
GeglBuffer *canvas_buffer,
gint x_offset,
gint y_offset)
{
- /* 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 = 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;
-
- iter = gegl_buffer_iterator_new (canvas_buffer, area, 0,
- babl_format ("Y float"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- gfloat *canvas_pixel = (gfloat *)iter->data[0];
- int iy, ix;
-
- 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 (
+ ¶ms,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA);
+}
- for (ix = 0; ix < iter->roi[0].width; ix++)
- {
- paint_pixel[3] *= *canvas_pixel;
- canvas_pixel += 1;
- paint_pixel += 4;
- }
- }
- }
- });
-}
+/* paint_mask_to_paint_buffer():
+ *
+ * A convenience wrapper around 'gimp_paint_core_loops_process()', performing
+ * just the PAINT_MASK_TO_PAINT_BUFFER algorithm.
+ */
void
paint_mask_to_paint_buffer (const GimpTempBuf *paint_mask,
@@ -248,64 +1247,27 @@ 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);
-
- 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);
-
- /* 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);
-
- 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;
+ GimpPaintCoreLoopsParams params = {};
- for (iy = 0; iy < height; iy++)
- {
- int mask_offset = iy * mask_stride;
- const guint8 *mask_pixel = &mask_data[mask_offset];
+ params.paint_buf = paint_buf;
- for (ix = 0; ix < width; ix++)
- {
- paint_pixel[3] *= (((gfloat)*mask_pixel) / 255.0f) * paint_opacity;
+ params.paint_mask = paint_mask;
+ params.paint_mask_offset_x = mask_x_offset;
+ params.paint_mask_offset_y = mask_y_offset;
- 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;
+ params.paint_opacity = paint_opacity;
- for (iy = 0; iy < height; iy++)
- {
- int mask_offset = iy * mask_stride;
- const gfloat *mask_pixel = &mask_data[mask_offset];
+ gimp_paint_core_loops_process (
+ ¶ms,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER);
+}
- for (ix = 0; ix < width; ix++)
- {
- paint_pixel[3] *= (*mask_pixel) * paint_opacity;
- mask_pixel += 1;
- paint_pixel += 4;
- }
- }
- }
- });
-}
+/* do_layer_blend():
+ *
+ * A convenience wrapper around 'gimp_paint_core_loops_process()', performing
+ * just the DO_LAYER_BLEND algorithm.
+ */
void
do_layer_blend (GeglBuffer *src_buffer,
@@ -319,100 +1281,42 @@ 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));
+ GimpPaintCoreLoopsParams params = {};
- 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);
-
- gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA,
- [=] (const GeglRectangle *area)
- {
- GeglBufferIterator *iter;
- GeglRectangle mask_area = *area;
+ params.paint_buf = paint_buf;
+ params.paint_buf_offset_x = x_offset;
+ params.paint_buf_offset_y = y_offset;
- mask_area.x -= mask_x_offset;
- mask_area.y -= mask_y_offset;
+ params.src_buffer = src_buffer;
+ params.dest_buffer = dst_buffer;
- iter = gegl_buffer_iterator_new (dst_buffer, area, 0,
- iterator_format,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+ params.mask_buffer = mask_buffer;
+ params.mask_offset_x = mask_x_offset;
+ params.mask_offset_y = mask_y_offset;
- gegl_buffer_iterator_add (iter, src_buffer, area, 0,
- iterator_format,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ params.image_opacity = opacity;
- if (mask_buffer)
- {
- gegl_buffer_iterator_add (iter, mask_buffer, &mask_area, 0,
- babl_format ("Y float"),
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- }
+ params.paint_mode = paint_mode;
- 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;
-
- paint_pixel = paint_data + ((iter->roi[0].y - roi.y) * paint_stride + iter->roi[0].x - roi.x) * 4;
-
- if (mask_buffer)
- mask_pixel = (gfloat *)iter->data[2];
+ gimp_paint_core_loops_process (
+ ¶ms,
+ GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND);
+}
- process_roi.x = iter->roi[0].x;
- process_roi.width = iter->roi[0].width;
- process_roi.height = 1;
- 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;
- }
- }
- });
-}
+/* mask_components_onto():
+ *
+ * Copies the contents of 'src_buffer' and 'aux_buffer' into 'dst_buffer', over
+ * 'roi'. Components set in 'mask' are copied from 'aux_buffer', while those
+ * not set in 'mask' are copied from 'src_buffer'. 'linear_mode' specifies
+ * whether to iterate over the buffers use a linear format. It should match
+ * the linear mode of the painted-to drawable, to avoid modifying masked-out
+ * components.
+ *
+ * Note that we don't integrate this function into the rest of the algorithm
+ * framework, since it uses a (potentially) different format when iterating
+ * over the buffers than the rest of the algorithms.
+ */
void
mask_components_onto (GeglBuffer *src_buffer,
@@ -467,5 +1371,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 (¶ms, 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]