diff --git a/app/operations/layer-modes/gimpoperationdissolve.c b/app/operations/layer-modes/gimpoperationdissolve.c index c71019d2ea..bd59ea8508 100644 --- a/app/operations/layer-modes/gimpoperationdissolve.c +++ b/app/operations/layer-modes/gimpoperationdissolve.c @@ -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; +} diff --git a/app/operations/layer-modes/gimpoperationlayermode.c b/app/operations/layer-modes/gimpoperationlayermode.c index bed3c4b703..2d547de861 100644 --- a/app/operations/layer-modes/gimpoperationlayermode.c +++ b/app/operations/layer-modes/gimpoperationlayermode.c @@ -44,21 +44,24 @@ enum }; -static void gimp_operation_layer_mode_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); -static void gimp_operation_layer_mode_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); +static void gimp_operation_layer_mode_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_operation_layer_mode_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); -static void gimp_operation_layer_mode_prepare (GeglOperation *operation); -static gboolean gimp_operation_layer_mode_process (GeglOperation *operation, - GeglOperationContext *context, - const gchar *output_prop, - const GeglRectangle *result, - gint level); +static void gimp_operation_layer_mode_prepare (GeglOperation *operation); +static gboolean gimp_operation_layer_mode_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + 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,21 +323,115 @@ gimp_operation_layer_mode_process (GeglOperation *operation, gint level) { GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation); + GObject *input; + GObject *aux; + gboolean has_input; + gboolean has_aux; - if (point->opacity == 0.0 || - ! gegl_operation_context_get_object (context, "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"); + + /* 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) { - GObject *input; - - /* get the raw values, this does not increase the reference count */ - input = gegl_operation_context_get_object (context, "input"); - - if (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)) { - gegl_operation_context_set_object (context, "output", input); + 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 * process function @@ -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); diff --git a/app/operations/layer-modes/gimpoperationlayermode.h b/app/operations/layer-modes/gimpoperationlayermode.h index 545203fbfc..9d69052e68 100644 --- a/app/operations/layer-modes/gimpoperationlayermode.h +++ b/app/operations/layer-modes/gimpoperationlayermode.h @@ -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 @@ -54,7 +62,9 @@ struct _GimpOperationLayerMode }; -GType gimp_operation_layer_mode_get_type (void) G_GNUC_CONST; +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, diff --git a/app/operations/layer-modes/gimpoperationnormal.c b/app/operations/layer-modes/gimpoperationnormal.c index 349549774b..3053236f05 100644 --- a/app/operations/layer-modes/gimpoperationnormal.c +++ b/app/operations/layer-modes/gimpoperationnormal.c @@ -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, diff --git a/app/operations/layer-modes/gimpoperationreplace.c b/app/operations/layer-modes/gimpoperationreplace.c index 2b1597d058..07a2c0ae43 100644 --- a/app/operations/layer-modes/gimpoperationreplace.c +++ b/app/operations/layer-modes/gimpoperationreplace.c @@ -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); + 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; +} diff --git a/app/operations/operations-enums.h b/app/operations/operations-enums.h index be1eb4e73b..073956230d 100644 --- a/app/operations/operations-enums.h +++ b/app/operations/operations-enums.h @@ -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__ */