app: move all special-case mode processing optimizations to GimpOperationLayerMode

Stuff like passing "input" directly if "aux"'s opacity is 0, etc.
Used to be partly handled by normal mode, even though it applies
to other modes too.

Adjust the logic for the new compositing modes.

Add a GimpLayerModeAffectMask enum, and a corresponding
get_affect_mask() function to GimpOperationLayerMode, which
specifies which of the op's inputs, if any, are affected by the
mode, apart from the overlapping regions.  Most modes affect only
the overlapping regions, but dissolve and replace also affect the
rest of the input.  This information is used for determining if
the optimizations are applicable.
This commit is contained in:
Ell 2017-02-02 10:53:09 -05:00
parent e957347dd6
commit 1214d4acf1
6 changed files with 206 additions and 88 deletions

View File

@ -32,6 +32,9 @@
#define RANDOM_TABLE_SIZE 4096
static GimpLayerModeAffectMask gimp_operation_dissolve_get_affect_mask (GimpOperationLayerMode *layer_mode);
G_DEFINE_TYPE (GimpOperationDissolve, gimp_operation_dissolve,
GIMP_TYPE_OPERATION_LAYER_MODE)
@ -44,11 +47,13 @@ gimp_operation_dissolve_class_init (GimpOperationDissolveClass *klass)
{
GeglOperationClass *operation_class;
GeglOperationPointComposer3Class *point_composer_class;
GimpOperationLayerModeClass *layer_mode_class;
GRand *gr;
gint i;
operation_class = GEGL_OPERATION_CLASS (klass);
point_composer_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass);
gegl_operation_class_set_keys (operation_class,
"name", "gimp:dissolve",
@ -58,6 +63,8 @@ gimp_operation_dissolve_class_init (GimpOperationDissolveClass *klass)
point_composer_class->process = gimp_operation_dissolve_process;
layer_mode_class->get_affect_mask = gimp_operation_dissolve_get_affect_mask;
/* generate a table of random seeds */
gr = g_rand_new_with_seed (314159265);
for (i = 0; i < RANDOM_TABLE_SIZE; i++)
@ -133,3 +140,9 @@ gimp_operation_dissolve_process (GeglOperation *op,
return TRUE;
}
static GimpLayerModeAffectMask
gimp_operation_dissolve_get_affect_mask (GimpOperationLayerMode *layer_mode)
{
return GIMP_LAYER_MODE_AFFECT_SRC;
}

View File

@ -60,6 +60,9 @@ static gboolean gimp_operation_layer_mode_process (GeglOperation *op
const GeglRectangle *result,
gint level);
static GimpLayerModeAffectMask
gimp_operation_layer_mode_real_get_affect_mask (GimpOperationLayerMode *layer_mode);
G_DEFINE_TYPE (GimpOperationLayerMode, gimp_operation_layer_mode,
GEGL_TYPE_OPERATION_POINT_COMPOSER3)
@ -152,6 +155,8 @@ gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass)
operation_class->process = gimp_operation_layer_mode_process;
point_composer3_class->process = gimp_operation_layer_mode_process_pixels;
klass->get_affect_mask = gimp_operation_layer_mode_real_get_affect_mask;
g_object_class_install_property (object_class, PROP_LAYER_MODE,
g_param_spec_enum ("layer-mode",
NULL, NULL,
@ -318,20 +323,114 @@ gimp_operation_layer_mode_process (GeglOperation *operation,
gint level)
{
GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
if (point->opacity == 0.0 ||
! gegl_operation_context_get_object (context, "aux"))
{
GObject *input;
GObject *aux;
gboolean has_input;
gboolean has_aux;
/* get the raw values, this does not increase the reference count */
/* get the raw values. this does not increase the reference count. */
input = gegl_operation_context_get_object (context, "input");
aux = gegl_operation_context_get_object (context, "aux");
if (input)
/* disregard 'input' if it's not included in the roi. */
has_input =
input &&
gegl_rectangle_intersect (NULL,
gegl_buffer_get_extent (GEGL_BUFFER (input)),
result);
/* disregard 'aux' if it's not included in the roi, or if it's fully
* transparent.
*/
has_aux =
aux &&
point->opacity != 0.0 &&
gegl_rectangle_intersect (NULL,
gegl_buffer_get_extent (GEGL_BUFFER (aux)),
result);
/* if there's no 'input' ... */
if (! has_input)
{
/* ... and there's 'aux', and the composite mode includes it ... */
if (has_aux &&
(point->composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
point->composite_mode == GIMP_LAYER_COMPOSITE_DST_ATOP))
{
GimpLayerModeAffectMask affect_mask;
affect_mask = gimp_operation_layer_mode_get_affect_mask (point);
/* ... and the op doesn't otherwise affect 'aux', or changes its
* alpha ...
*/
if (! (affect_mask & GIMP_LAYER_MODE_AFFECT_SRC) &&
point->opacity == 1.0 &&
! gegl_operation_context_get_object (context, "aux2"))
{
/* pass 'aux' directly as output; */
gegl_operation_context_set_object (context, "output", aux);
return TRUE;
}
/* otherwise, if the op affects 'aux', or changes its alpha, process
* it even though there's no 'input';
*/
}
/* otherwise, there's no 'aux', or the composite mode doesn't include it,
* and so ...
*/
else
{
/* ... the output is empty. */
gegl_operation_context_set_object (context, "output", NULL);
return TRUE;
}
}
/* otherwise, if there's 'input' but no 'aux' ... */
else if (! has_aux)
{
/* ... and the composite mode includes 'input' ... */
if (point->composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
point->composite_mode == GIMP_LAYER_COMPOSITE_SRC_ATOP)
{
GimpLayerModeAffectMask affect_mask;
affect_mask = gimp_operation_layer_mode_get_affect_mask (point);
/* ... and the op doesn't otherwise affect 'input' ... */
if (! (affect_mask & GIMP_LAYER_MODE_AFFECT_DST))
{
/* pass 'input' directly as output; */
gegl_operation_context_set_object (context, "output", input);
return TRUE;
}
/* otherwise, if the op affects 'input', process it even though
* there's no 'aux';
*/
}
/* otherwise, the output is fully transparent, but we process it anyway
* to maintain the 'input' color values.
*/
}
/* FIXME: we don't actually handle the case where one of the inputs
* is NULL -- it'll just segfault. 'input' is not expected to be NULL,
* but 'aux' might be, currently.
*/
if (! input || ! aux)
{
GObject *empty = G_OBJECT (gegl_buffer_new (NULL, NULL));
if (! input) gegl_operation_context_set_object (context, "input", empty);
if (! aux) gegl_operation_context_set_object (context, "aux", empty);
if (! input && ! aux)
gegl_object_set_has_forked (G_OBJECT (empty));
g_object_unref (empty);
}
/* chain up, which will create the needed buffers for our actual
@ -342,6 +441,29 @@ gimp_operation_layer_mode_process (GeglOperation *operation,
level);
}
static GimpLayerModeAffectMask
gimp_operation_layer_mode_real_get_affect_mask (GimpOperationLayerMode *layer_mode)
{
/* most modes only affect the overlapping regions. */
return GIMP_LAYER_MODE_AFFECT_NONE;
}
/* public functions */
GimpLayerModeAffectMask
gimp_operation_layer_mode_get_affect_mask (GimpOperationLayerMode *layer_mode)
{
g_return_val_if_fail (GIMP_IS_OPERATION_LAYER_MODE (layer_mode),
GIMP_LAYER_MODE_AFFECT_NONE);
return GIMP_OPERATION_LAYER_MODE_GET_CLASS (layer_mode)->get_affect_mask (layer_mode);
}
/* compositing and blending functions */
static inline GimpBlendFunc gimp_layer_mode_get_blend_fun (GimpLayerMode mode);

View File

@ -38,6 +38,14 @@ typedef struct _GimpOperationLayerModeClass GimpOperationLayerModeClass;
struct _GimpOperationLayerModeClass
{
GeglOperationPointComposer3Class parent_class;
/* virtual functions */
/* Returns the set of inputs that the layer mode affects, apart
* from the overlapping regions. Returns an empty set by default,
* which is suitable for almost all layer modes.
*/
GimpLayerModeAffectMask (* get_affect_mask) (GimpOperationLayerMode *layer_mode);
};
struct _GimpOperationLayerMode
@ -56,6 +64,8 @@ struct _GimpOperationLayerMode
GType gimp_operation_layer_mode_get_type (void) G_GNUC_CONST;
GimpLayerModeAffectMask gimp_operation_layer_mode_get_affect_mask (GimpOperationLayerMode *layer_mode);
gboolean
gimp_operation_layer_mode_process_pixels (GeglOperation *operation,
void *in,

View File

@ -30,12 +30,6 @@
#include "gimpoperationnormal.h"
static gboolean gimp_operation_normal_parent_process (GeglOperation *operation,
GeglOperationContext *context,
const gchar *output_prop,
const GeglRectangle *result,
gint level);
G_DEFINE_TYPE (GimpOperationNormal, gimp_operation_normal,
GIMP_TYPE_OPERATION_LAYER_MODE)
@ -77,9 +71,6 @@ gimp_operation_normal_class_init (GimpOperationNormalClass *klass)
"reference-composition", reference_xml,
NULL);
operation_class->process = gimp_operation_normal_parent_process;
gimp_operation_normal_process = gimp_operation_normal_process_core;
#if COMPILE_SSE2_INTRINISICS
@ -100,59 +91,6 @@ gimp_operation_normal_init (GimpOperationNormal *self)
{
}
static gboolean
gimp_operation_normal_parent_process (GeglOperation *operation,
GeglOperationContext *context,
const gchar *output_prop,
const GeglRectangle *result,
gint level)
{
GimpOperationLayerMode *layer_mode = GIMP_OPERATION_LAYER_MODE (operation);
if (layer_mode->opacity == 1.0 &&
! gegl_operation_context_get_object (context, "aux2"))
{
const GeglRectangle *in_extent = NULL;
const GeglRectangle *aux_extent = NULL;
GObject *input;
GObject *aux;
/* get the raw values this does not increase the reference count */
input = gegl_operation_context_get_object (context, "input");
aux = gegl_operation_context_get_object (context, "aux");
/* pass the input/aux buffers directly through if they are not
* overlapping
*/
if (input)
in_extent = gegl_buffer_get_abyss (GEGL_BUFFER (input));
if (! input ||
(aux && ! gegl_rectangle_intersect (NULL, in_extent, result)))
{
gegl_operation_context_set_object (context, "output", aux);
return TRUE;
}
if (aux)
aux_extent = gegl_buffer_get_abyss (GEGL_BUFFER (aux));
if (! aux ||
(input && ! gegl_rectangle_intersect (NULL, aux_extent, result)))
{
gegl_operation_context_set_object (context, "output", input);
return TRUE;
}
}
/* chain up, which will create the needed buffers for our actual
* process function
*/
return GEGL_OPERATION_CLASS (parent_class)->process (operation, context,
output_prop, result,
level);
}
gboolean
gimp_operation_normal_process_core (GeglOperation *op,
void *in_p,

View File

@ -27,6 +27,9 @@
#include "gimpoperationreplace.h"
static GimpLayerModeAffectMask gimp_operation_replace_get_affect_mask (GimpOperationLayerMode *layer_mode);
G_DEFINE_TYPE (GimpOperationReplace, gimp_operation_replace,
GIMP_TYPE_OPERATION_LAYER_MODE)
@ -36,9 +39,11 @@ gimp_operation_replace_class_init (GimpOperationReplaceClass *klass)
{
GeglOperationClass *operation_class;
GeglOperationPointComposer3Class *point_class;
GimpOperationLayerModeClass *layer_mode_class;
operation_class = GEGL_OPERATION_CLASS (klass);
point_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass);
gegl_operation_class_set_keys (operation_class,
"name", "gimp:replace",
@ -46,6 +51,8 @@ gimp_operation_replace_class_init (GimpOperationReplaceClass *klass)
NULL);
point_class->process = gimp_operation_replace_process;
layer_mode_class->get_affect_mask = gimp_operation_replace_get_affect_mask;
}
static void
@ -107,3 +114,18 @@ gimp_operation_replace_process (GeglOperation *op,
return TRUE;
}
static GimpLayerModeAffectMask
gimp_operation_replace_get_affect_mask (GimpOperationLayerMode *layer_mode)
{
GimpLayerModeAffectMask affect_mask = GIMP_LAYER_MODE_AFFECT_NONE;
if (layer_mode->opacity != 0.0)
affect_mask |= GIMP_LAYER_MODE_AFFECT_DST;
/* if opacity != 1.0, or we have a mask, thne we also affect SRC, but this is
* considered the case anyway, so no need for special handling.
*/
return affect_mask;
}

View File

@ -47,4 +47,17 @@ typedef enum
} GimpLayerCompositeMode;
/*
* non-registered enums; register them if needed
*/
typedef enum /*< pdb-skip, skip >*/
{
GIMP_LAYER_MODE_AFFECT_NONE = 0,
GIMP_LAYER_MODE_AFFECT_DST = 1 << 0,
GIMP_LAYER_MODE_AFFECT_SRC = 1 << 1
} GimpLayerModeAffectMask;
#endif /* __OPERATIONS_ENUMS_H__ */