2270 lines
72 KiB
C++
2270 lines
72 KiB
C++
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* 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 "gegl/pika-babl.h"
|
|
|
|
#include "operations/layer-modes/pika-layer-modes.h"
|
|
|
|
#include "core/pikatempbuf.h"
|
|
|
|
#include "operations/pikaoperationmaskcomponents.h"
|
|
|
|
#include "operations/layer-modes/pikaoperationlayermode.h"
|
|
|
|
#include "pikapaintcore-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, pika_paint_core_loops_process(), which can be used to perform
|
|
* multiple algorithms in a row. This function takes a pointer to a
|
|
* PikaPaintCoreLoopsParams structure, providing the parameters for the
|
|
* algorithms, and a PikaPaintCoreLoopsAlgorithm 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
|
|
* pika_paint_core_loops_process().
|
|
*
|
|
* We use some C++ magic to statically generate specialized versions of
|
|
* pika_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. pika_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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *params) :
|
|
Base (params)
|
|
{
|
|
paint_stride = pika_temp_buf_get_width (params->paint_buf) * 4;
|
|
paint_data = (paint_type *) pika_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 PikaPaintCoreLoopsParams *params) :
|
|
Base (params)
|
|
{
|
|
mask_stride = pika_temp_buf_get_width (params->paint_mask);
|
|
mask_data =
|
|
(const mask_type *) pika_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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm algorithms,
|
|
identity<Algorithm> algorithm) const
|
|
{
|
|
const Babl *mask_format = pika_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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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,
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
|
|
PIKA_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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_BUFFER |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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,
|
|
PIKA_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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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,
|
|
PIKA_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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUF_ALPHA |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK;
|
|
|
|
explicit
|
|
PaintMaskToPaintBufAlpha (const PikaPaintCoreLoopsParams *params) :
|
|
Base (params)
|
|
{
|
|
/* Validate that the paint buffer is within the bounds of the paint mask */
|
|
g_return_if_fail (pika_temp_buf_get_width (params->paint_buf) <=
|
|
pika_temp_buf_get_width (params->paint_mask) -
|
|
params->paint_mask_offset_x);
|
|
g_return_if_fail (pika_temp_buf_get_height (params->paint_buf) <=
|
|
pika_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 PikaPaintCoreLoopsParams *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,
|
|
PIKA_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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 =
|
|
PIKA_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,
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_COMP_MASK,
|
|
decltype (dispatch_comp_mask)
|
|
>;
|
|
using DispatchIndirect = BasicDispatch<
|
|
AlgorithmIndirect,
|
|
PIKA_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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm 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 |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK |
|
|
PIKA_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 =
|
|
PIKA_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,
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_COMP_MASK,
|
|
decltype (dispatch_comp_mask)
|
|
>;
|
|
using DispatchIndirect = BasicDispatch<
|
|
AlgorithmIndirect,
|
|
PIKA_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 PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm algorithms,
|
|
identity<Algorithm> algorithm) const
|
|
{
|
|
if ((algorithms & mask) == mask)
|
|
{
|
|
dispatch (
|
|
[&] (auto algorithm)
|
|
{
|
|
using NewAlgorithm = typename decltype (algorithm)::type;
|
|
|
|
if (params->paint_opacity == PIKA_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 |
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND;
|
|
|
|
static constexpr gint max_n_iterators = Base::max_n_iterators + 2;
|
|
|
|
const Babl *iterator_format;
|
|
PikaOperationLayerMode *layer_mode = NULL;
|
|
|
|
explicit
|
|
DoLayerBlend (const PikaPaintCoreLoopsParams *params) :
|
|
Base (params)
|
|
{
|
|
layer_mode = PIKA_OPERATION_LAYER_MODE (pika_layer_mode_get_operation (params->paint_mode));
|
|
layer_mode->opacity = params->image_opacity;
|
|
|
|
iterator_format = pika_layer_mode_get_format (params->paint_mode,
|
|
layer_mode->blend_space,
|
|
layer_mode->composite_space,
|
|
layer_mode->composite_mode,
|
|
pika_temp_buf_get_format (params->paint_buf));
|
|
|
|
g_return_if_fail (pika_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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,
|
|
PIKA_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 |
|
|
PIKA_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 PikaPaintCoreLoopsParams *params) :
|
|
Base (params)
|
|
{
|
|
format = pika_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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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 PikaPaintCoreLoopsParams *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;
|
|
}
|
|
|
|
pika_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 PikaPaintCoreLoopsParams *params,
|
|
State<Derived> *state) const
|
|
{
|
|
if (comp_fish)
|
|
gegl_scratch_free (state->comp_pixel);
|
|
|
|
Base::finalize_step (params, state);
|
|
}
|
|
};
|
|
|
|
static AlgorithmDispatch<
|
|
MaskComponents,
|
|
PIKA_PAINT_CORE_LOOPS_ALGORITHM_MASK_COMPONENTS,
|
|
decltype (dispatch_temp_comp_buffer)
|
|
>
|
|
dispatch_mask_components;
|
|
|
|
|
|
/* pika_paint_core_loops_process():
|
|
*
|
|
* Performs the set of algorithms requested in 'algorithms', specified as a
|
|
* bitwise-OR of 'PikaPaintCoreLoopsAlgorithm' 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
|
|
* 'PikaPaintCoreLoopsAlgorithm' enum.
|
|
*/
|
|
|
|
void
|
|
pika_paint_core_loops_process (const PikaPaintCoreLoopsParams *params,
|
|
PikaPaintCoreLoopsAlgorithm algorithms)
|
|
{
|
|
GeglRectangle roi;
|
|
|
|
if (params->paint_buf)
|
|
{
|
|
roi.x = params->paint_buf_offset_x;
|
|
roi.y = params->paint_buf_offset_y;
|
|
roi.width = pika_temp_buf_get_width (params->paint_buf);
|
|
roi.height = pika_temp_buf_get_height (params->paint_buf);
|
|
}
|
|
else
|
|
{
|
|
roi.x = params->paint_buf_offset_x;
|
|
roi.y = params->paint_buf_offset_y;
|
|
roi.width = pika_temp_buf_get_width (params->paint_mask) -
|
|
params->paint_mask_offset_x;
|
|
roi.height = pika_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);
|
|
}
|