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.
This commit is contained in:
Ell 2018-04-14 19:02:21 -04:00
parent 76eedf2198
commit f2a1fd5bf0
3 changed files with 1330 additions and 385 deletions

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,48 @@
#define __GIMP_PAINT_CORE_LOOPS_H__
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,

View File

@ -864,77 +864,78 @@ gimp_paint_core_paste (GimpPaintCore *core,
}
else
{
GimpTempBuf *paint_buf = gimp_gegl_buffer_get_temp_buf (core->paint_buffer);
GeglBuffer *dest_buffer;
GeglBuffer *src_buffer;
GimpPaintCoreLoopsParams params = {};
GimpPaintCoreLoopsAlgorithm algorithms = GIMP_PAINT_CORE_LOOPS_ALGORITHM_NONE;
if (! paint_buf)
params.paint_buf = gimp_gegl_buffer_get_temp_buf (core->paint_buffer);
params.paint_buf_offset_x = core->paint_buffer_x;
params.paint_buf_offset_y = core->paint_buffer_y;
if (! params.paint_buf)
return;
if (core->comp_buffer)
dest_buffer = core->comp_buffer;
params.dest_buffer = core->comp_buffer;
else
dest_buffer = gimp_drawable_get_buffer (drawable);
params.dest_buffer = gimp_drawable_get_buffer (drawable);
if (mode == GIMP_PAINT_CONSTANT)
{
params.canvas_buffer = core->canvas_buffer;
/* This step is skipped by the ink tool, which writes
* directly to canvas_buffer
*/
if (paint_mask != NULL)
{
/* Mix paint mask and canvas_buffer */
combine_paint_mask_to_canvas_mask (paint_mask,
paint_mask_offset_x,
paint_mask_offset_y,
core->canvas_buffer,
core->paint_buffer_x,
core->paint_buffer_y,
paint_opacity,
GIMP_IS_AIRBRUSH (core));
params.paint_mask = paint_mask;
params.paint_mask_offset_x = paint_mask_offset_x;
params.paint_mask_offset_y = paint_mask_offset_y;
params.stipple = GIMP_IS_AIRBRUSH (core);
params.paint_opacity = paint_opacity;
algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_COMBINE_PAINT_MASK_TO_CANVAS_MASK;
}
/* Write canvas_buffer to paint_buf */
canvas_buffer_to_paint_buf_alpha (paint_buf,
core->canvas_buffer,
core->paint_buffer_x,
core->paint_buffer_y);
algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_CANVAS_BUFFER_TO_PAINT_BUF_ALPHA;
/* undo buf -> paint_buf -> dest_buffer */
src_buffer = core->undo_buffer;
params.src_buffer = core->undo_buffer;
}
else
{
g_return_if_fail (paint_mask);
/* Write paint_mask to paint_buf, does not modify canvas_buffer */
paint_mask_to_paint_buffer (paint_mask,
paint_mask_offset_x,
paint_mask_offset_y,
paint_buf,
paint_opacity);
params.paint_mask = paint_mask;
params.paint_mask_offset_x = paint_mask_offset_x;
params.paint_mask_offset_y = paint_mask_offset_y;
params.paint_opacity = paint_opacity;
algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_PAINT_MASK_TO_PAINT_BUFFER;
/* dest_buffer -> paint_buf -> dest_buffer */
if (core->comp_buffer)
src_buffer = gimp_drawable_get_buffer (drawable);
params.src_buffer = gimp_drawable_get_buffer (drawable);
else
src_buffer = dest_buffer;
params.src_buffer = params.dest_buffer;
}
do_layer_blend (src_buffer,
dest_buffer,
paint_buf,
core->mask_buffer,
image_opacity,
core->paint_buffer_x,
core->paint_buffer_y,
core->mask_x_offset,
core->mask_y_offset,
paint_mode);
params.mask_buffer = core->mask_buffer;
params.mask_offset_x = core->mask_x_offset;
params.mask_offset_y = core->mask_y_offset;
params.image_opacity = image_opacity;
params.paint_mode = paint_mode;
algorithms |= GIMP_PAINT_CORE_LOOPS_ALGORITHM_DO_LAYER_BLEND;
gimp_paint_core_loops_process (&params, algorithms);
if (core->comp_buffer)
{
mask_components_onto (src_buffer,
mask_components_onto (params.src_buffer,
core->comp_buffer,
gimp_drawable_get_buffer (drawable),
GEGL_RECTANGLE (core->paint_buffer_x,