
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.
1951 lines
54 KiB
C
1951 lines
54 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimpoperationlayermode.c
|
|
* Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
|
|
* Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl-plugin.h>
|
|
#include <cairo.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "../operations-types.h"
|
|
|
|
#include "gimpoperationlayermode.h"
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_LAYER_MODE,
|
|
PROP_LINEAR,
|
|
PROP_OPACITY,
|
|
PROP_BLEND_SPACE,
|
|
PROP_COMPOSITE_SPACE,
|
|
PROP_COMPOSITE_MODE
|
|
};
|
|
|
|
|
|
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 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)
|
|
|
|
#define parent_class gimp_operation_layer_mode_parent_class
|
|
|
|
|
|
const Babl *_gimp_fish_rgba_to_perceptual = NULL;
|
|
const Babl *_gimp_fish_perceptual_to_rgba = NULL;
|
|
const Babl *_gimp_fish_perceptual_to_laba = NULL;
|
|
const Babl *_gimp_fish_rgba_to_laba = NULL;
|
|
const Babl *_gimp_fish_laba_to_rgba = NULL;
|
|
const Babl *_gimp_fish_laba_to_perceptual = NULL;
|
|
|
|
typedef void (*CompFun)(gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
float opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
|
|
|
|
static inline void compfun_src_atop (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
static inline void compfun_dst_atop (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
static inline void compfun_src_in (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
static inline void compfun_src_over (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
|
|
static CompFun compfun_src_atop_dispatch = compfun_src_atop;
|
|
static CompFun compfun_dst_atop_dispatch = compfun_dst_atop;
|
|
static CompFun compfun_src_in_dispatch = compfun_src_in;
|
|
static CompFun compfun_src_over_dispatch = compfun_src_over;
|
|
#if COMPILE_SSE2_INTRINISICS
|
|
|
|
static inline void compfun_src_atop_sse2 (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples);
|
|
#endif
|
|
|
|
static void
|
|
gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GeglOperationClass *operation_class;
|
|
GeglOperationPointComposer3Class *point_composer3_class;
|
|
|
|
#if COMPILE_SSE2_INTRINISICS
|
|
if (gimp_cpu_accel_get_support () & GIMP_CPU_ACCEL_X86_SSE2)
|
|
compfun_src_atop_dispatch = compfun_src_atop_sse2;
|
|
#endif
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
operation_class = GEGL_OPERATION_CLASS (klass);
|
|
point_composer3_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
|
|
|
|
gegl_operation_class_set_keys (operation_class,
|
|
"name", "gimp:layer-mode", NULL);
|
|
|
|
object_class->set_property = gimp_operation_layer_mode_set_property;
|
|
object_class->get_property = gimp_operation_layer_mode_get_property;
|
|
|
|
operation_class->prepare = gimp_operation_layer_mode_prepare;
|
|
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,
|
|
GIMP_TYPE_LAYER_MODE,
|
|
GIMP_LAYER_MODE_NORMAL,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_LINEAR,
|
|
g_param_spec_boolean ("linear",
|
|
NULL, NULL,
|
|
FALSE,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_OPACITY,
|
|
g_param_spec_double ("opacity",
|
|
NULL, NULL,
|
|
0.0, 1.0, 1.0,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_BLEND_SPACE,
|
|
g_param_spec_enum ("blend-space",
|
|
NULL, NULL,
|
|
GIMP_TYPE_LAYER_COLOR_SPACE,
|
|
GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_COMPOSITE_SPACE,
|
|
g_param_spec_enum ("composite-space",
|
|
NULL, NULL,
|
|
GIMP_TYPE_LAYER_COLOR_SPACE,
|
|
GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_COMPOSITE_MODE,
|
|
g_param_spec_enum ("composite-mode",
|
|
NULL, NULL,
|
|
GIMP_TYPE_LAYER_COMPOSITE_MODE,
|
|
GIMP_LAYER_COMPOSITE_SRC_OVER,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
_gimp_fish_rgba_to_perceptual = babl_fish ("RGBA float", "R'G'B'A float");
|
|
_gimp_fish_perceptual_to_rgba = babl_fish ("R'G'B'A float", "RGBA float");
|
|
_gimp_fish_perceptual_to_laba = babl_fish ("R'G'B'A float", "CIE Lab alpha float");
|
|
|
|
_gimp_fish_rgba_to_laba = babl_fish ("RGBA float", "CIE Lab alpha float");
|
|
_gimp_fish_laba_to_rgba = babl_fish ("CIE Lab alpha float", "RGBA float");
|
|
_gimp_fish_laba_to_perceptual = babl_fish ("CIE Lab alpha float", "R'G'B'A float");
|
|
}
|
|
|
|
static void
|
|
gimp_operation_layer_mode_init (GimpOperationLayerMode *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gimp_operation_layer_mode_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_LAYER_MODE:
|
|
self->layer_mode = g_value_get_enum (value);
|
|
break;
|
|
|
|
case PROP_LINEAR:
|
|
self->linear = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_OPACITY:
|
|
self->opacity = g_value_get_double (value);
|
|
break;
|
|
|
|
case PROP_BLEND_SPACE:
|
|
self->blend_space = g_value_get_enum (value);
|
|
break;
|
|
|
|
case PROP_COMPOSITE_SPACE:
|
|
self->composite_space = g_value_get_enum (value);
|
|
break;
|
|
|
|
case PROP_COMPOSITE_MODE:
|
|
self->composite_mode = g_value_get_enum (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_operation_layer_mode_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_LAYER_MODE:
|
|
g_value_set_enum (value, self->layer_mode);
|
|
break;
|
|
|
|
case PROP_LINEAR:
|
|
g_value_set_boolean (value, self->linear);
|
|
break;
|
|
|
|
case PROP_OPACITY:
|
|
g_value_set_double (value, self->opacity);
|
|
break;
|
|
|
|
case PROP_BLEND_SPACE:
|
|
g_value_set_enum (value, self->blend_space);
|
|
break;
|
|
|
|
case PROP_COMPOSITE_SPACE:
|
|
g_value_set_enum (value, self->composite_space);
|
|
break;
|
|
|
|
case PROP_COMPOSITE_MODE:
|
|
g_value_set_enum (value, self->composite_mode);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_operation_layer_mode_prepare (GeglOperation *operation)
|
|
{
|
|
GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (operation);
|
|
const Babl *format;
|
|
|
|
if (self->linear)
|
|
format = babl_format ("RGBA float");
|
|
else
|
|
format = babl_format ("R'G'B'A float");
|
|
|
|
gegl_operation_set_format (operation, "input", format);
|
|
gegl_operation_set_format (operation, "output", format);
|
|
gegl_operation_set_format (operation, "aux", format);
|
|
gegl_operation_set_format (operation, "aux2", babl_format ("Y float"));
|
|
}
|
|
|
|
static gboolean
|
|
gimp_operation_layer_mode_process (GeglOperation *operation,
|
|
GeglOperationContext *context,
|
|
const gchar *output_prop,
|
|
const GeglRectangle *result,
|
|
gint level)
|
|
{
|
|
GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
|
|
GObject *input;
|
|
GObject *aux;
|
|
gboolean has_input;
|
|
gboolean has_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)
|
|
{
|
|
/* ... 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
|
|
* process function
|
|
*/
|
|
return GEGL_OPERATION_CLASS (parent_class)->process (operation, context,
|
|
output_prop, result,
|
|
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);
|
|
|
|
static inline void gimp_composite_blend (gpointer op,
|
|
gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *mask,
|
|
gfloat *out,
|
|
glong samples,
|
|
GimpBlendFunc blend_func);
|
|
gboolean
|
|
gimp_operation_layer_mode_process_pixels (GeglOperation *operation,
|
|
void *in,
|
|
void *layer,
|
|
void *mask,
|
|
void *out,
|
|
glong samples,
|
|
const GeglRectangle *roi,
|
|
gint level)
|
|
{
|
|
GimpOperationLayerMode *layer_mode = (GimpOperationLayerMode*)(operation);
|
|
gimp_composite_blend (operation, in, layer, mask, out, samples,
|
|
gimp_layer_mode_get_blend_fun (layer_mode->layer_mode));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
compfun_src_atop (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
gfloat layer_alpha = layer[ALPHA] * opacity;
|
|
|
|
if (mask)
|
|
layer_alpha *= *mask;
|
|
|
|
if (layer_alpha == 0.0f)
|
|
{
|
|
out[RED] = in[RED];
|
|
out[GREEN] = in[GREEN];
|
|
out[BLUE] = in[BLUE];
|
|
}
|
|
else
|
|
{
|
|
gint b;
|
|
|
|
for (b = RED; b < ALPHA; b++)
|
|
out[b] = layer[b] * layer_alpha + in[b] * (1.0f - layer_alpha);
|
|
}
|
|
|
|
out[ALPHA] = in[ALPHA];
|
|
|
|
in += 4;
|
|
out += 4;
|
|
layer += 4;
|
|
|
|
if (mask)
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
compfun_src_over (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
gfloat new_alpha;
|
|
gfloat layer_alpha = layer[ALPHA] * opacity;
|
|
|
|
if (mask)
|
|
layer_alpha *= *mask;
|
|
|
|
new_alpha = layer_alpha + (1.0f - layer_alpha) * in[ALPHA];
|
|
|
|
if (layer_alpha == 0.0f || new_alpha == 0.0f)
|
|
{
|
|
out[RED] = in[RED];
|
|
out[GREEN] = in[GREEN];
|
|
out[BLUE] = in[BLUE];
|
|
}
|
|
else
|
|
{
|
|
gfloat ratio = layer_alpha / new_alpha;
|
|
gint b;
|
|
|
|
for (b = RED; b < ALPHA; b++)
|
|
out[b] = ratio * (in[ALPHA] * (comp[b] - layer[b]) + layer[b] - in[b]) + in[b];
|
|
}
|
|
|
|
out[ALPHA] = new_alpha;
|
|
|
|
in += 4;
|
|
layer += 4;
|
|
comp += 4;
|
|
out += 4;
|
|
|
|
if (mask)
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
compfun_dst_atop (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
gfloat layer_alpha = layer[ALPHA] * opacity;
|
|
|
|
if (mask)
|
|
layer_alpha *= *mask;
|
|
|
|
if (layer_alpha == 0.0f)
|
|
{
|
|
out[RED] = in[RED];
|
|
out[GREEN] = in[GREEN];
|
|
out[BLUE] = in[BLUE];
|
|
}
|
|
else
|
|
{
|
|
gint b;
|
|
|
|
for (b = RED; b < ALPHA; b++)
|
|
out[b] = comp[b] * in[ALPHA] + layer[b] * (1.0f - in[ALPHA]);
|
|
}
|
|
|
|
out[ALPHA] = layer_alpha;
|
|
|
|
in += 4;
|
|
layer += 4;
|
|
comp += 4;
|
|
out += 4;
|
|
|
|
if (mask)
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
compfun_src_in (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
gfloat new_alpha = in[ALPHA] * layer[ALPHA] * opacity;
|
|
|
|
if (mask)
|
|
new_alpha *= *mask;
|
|
|
|
if (new_alpha == 0.0f)
|
|
{
|
|
out[RED] = in[RED];
|
|
out[GREEN] = in[GREEN];
|
|
out[BLUE] = in[BLUE];
|
|
}
|
|
else
|
|
{
|
|
out[RED] = layer[RED];
|
|
out[GREEN] = layer[GREEN];
|
|
out[BLUE] = layer[BLUE];
|
|
}
|
|
|
|
out[ALPHA] = new_alpha;
|
|
|
|
in += 4;
|
|
out += 4;
|
|
layer += 4;
|
|
|
|
if (mask)
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
#if COMPILE_SSE2_INTRINISICS
|
|
|
|
#include <emmintrin.h>
|
|
|
|
static inline void
|
|
compfun_src_atop_sse2 (gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *comp,
|
|
gfloat *mask,
|
|
gfloat opacity,
|
|
gfloat *out,
|
|
gint samples)
|
|
{
|
|
if ((((uintptr_t)in) | /* alignment check */
|
|
((uintptr_t)mask) |
|
|
((uintptr_t)comp) |
|
|
((uintptr_t)layer) |
|
|
((uintptr_t)out) ) & 0x0F)
|
|
{
|
|
return compfun_src_atop (in, layer, comp, mask, opacity, out, samples);
|
|
}
|
|
else
|
|
{
|
|
const __v4sf *v_in = (const __v4sf*) in;
|
|
const __v4sf *v_layer = (const __v4sf*) layer;
|
|
__v4sf *v_out = (__v4sf*) out;
|
|
const __v4sf v_one = _mm_set1_ps (1.0f);
|
|
const __v4sf v_opacity = _mm_set1_ps (opacity);
|
|
|
|
while (samples--)
|
|
{
|
|
__v4sf alpha, rgba_in, rgba_layer;
|
|
|
|
rgba_in = *v_in ++;
|
|
rgba_layer = *v_layer++;
|
|
|
|
alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_layer,_MM_SHUFFLE(3,3,3,3)) * v_opacity;
|
|
|
|
if (mask)
|
|
{
|
|
alpha = alpha * _mm_set1_ps (*mask++);
|
|
}
|
|
|
|
if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
|
|
{
|
|
__v4sf out_pixel, out_pixel_rbaa, out_alpha;
|
|
|
|
out_alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_in,_MM_SHUFFLE(3,3,3,3));
|
|
out_pixel = rgba_layer * alpha + rgba_in * (v_one - alpha);
|
|
out_pixel_rbaa = _mm_shuffle_ps (out_pixel, out_alpha, _MM_SHUFFLE (3, 3, 2, 0));
|
|
out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
|
|
|
|
*v_out++ = out_pixel;
|
|
}
|
|
else
|
|
{
|
|
*v_out ++ = rgba_in;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static inline void
|
|
gimp_composite_blend (gpointer op,
|
|
gfloat *in,
|
|
gfloat *layer,
|
|
gfloat *mask,
|
|
gfloat *out,
|
|
glong samples,
|
|
GimpBlendFunc blend_func)
|
|
{
|
|
GimpOperationLayerMode *layer_mode = op;
|
|
gfloat opacity = layer_mode->opacity;
|
|
GimpLayerColorSpace blend_space = layer_mode->blend_space;
|
|
GimpLayerColorSpace composite_space= layer_mode->composite_space;
|
|
GimpLayerCompositeMode composite_mode = layer_mode->composite_mode;
|
|
|
|
gfloat *blend_in = in;
|
|
gfloat *blend_layer = layer;
|
|
gfloat *blend_out = out;
|
|
|
|
gfloat *composite_in = NULL;
|
|
gfloat *composite_layer = NULL;
|
|
|
|
gboolean composite_needs_in_color =
|
|
composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
|
|
composite_mode == GIMP_LAYER_COMPOSITE_SRC_ATOP;
|
|
gboolean composite_needs_layer_color =
|
|
composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
|
|
composite_mode == GIMP_LAYER_COMPOSITE_DST_ATOP;
|
|
|
|
const Babl *fish_to_blend = NULL;
|
|
const Babl *fish_to_composite = NULL;
|
|
const Babl *fish_from_composite = NULL;
|
|
|
|
switch (blend_space)
|
|
{
|
|
default:
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
|
|
fish_to_blend = NULL;
|
|
switch (composite_space)
|
|
{
|
|
case GIMP_LAYER_COLOR_SPACE_LAB:
|
|
fish_to_composite = _gimp_fish_rgba_to_laba;
|
|
fish_from_composite = _gimp_fish_laba_to_rgba;
|
|
break;
|
|
default:
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
|
|
fish_to_composite = NULL;
|
|
fish_from_composite = NULL;
|
|
break;
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
|
|
fish_to_composite = _gimp_fish_rgba_to_perceptual;
|
|
fish_from_composite = _gimp_fish_perceptual_to_rgba;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GIMP_LAYER_COLOR_SPACE_LAB:
|
|
fish_to_blend = _gimp_fish_rgba_to_laba;
|
|
switch (composite_space)
|
|
{
|
|
case GIMP_LAYER_COLOR_SPACE_LAB:
|
|
default:
|
|
fish_to_composite = NULL;
|
|
fish_from_composite = _gimp_fish_laba_to_rgba;
|
|
break;
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
|
|
fish_to_composite = _gimp_fish_laba_to_rgba;
|
|
fish_from_composite = NULL;
|
|
break;
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
|
|
fish_to_composite = _gimp_fish_laba_to_perceptual;
|
|
fish_from_composite = _gimp_fish_perceptual_to_rgba;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
|
|
fish_to_blend = _gimp_fish_rgba_to_perceptual;
|
|
switch (composite_space)
|
|
{
|
|
case GIMP_LAYER_COLOR_SPACE_LAB:
|
|
default:
|
|
fish_to_composite = _gimp_fish_perceptual_to_laba;
|
|
fish_from_composite = NULL;
|
|
break;
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
|
|
fish_to_composite = _gimp_fish_perceptual_to_rgba;
|
|
fish_from_composite = NULL;
|
|
break;
|
|
case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
|
|
fish_to_composite = NULL;
|
|
fish_from_composite = _gimp_fish_perceptual_to_rgba;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (in == out) /* in-place detected, avoid clobbering since we need to
|
|
read it for the compositing stage */
|
|
blend_out = g_alloca (sizeof (gfloat) * 4 * samples);
|
|
|
|
if (fish_to_blend)
|
|
{
|
|
if (in != out || (composite_needs_in_color &&
|
|
composite_space == GIMP_LAYER_COLOR_SPACE_RGB_LINEAR))
|
|
{
|
|
/* don't convert input in-place if we're not doing in-place output,
|
|
* or if we're going to need the original input for compositing.
|
|
*/
|
|
blend_in = g_alloca (sizeof (gfloat) * 4 * samples);
|
|
}
|
|
blend_layer = g_alloca (sizeof (gfloat) * 4 * samples);
|
|
|
|
babl_process (fish_to_blend, in, blend_in, samples);
|
|
babl_process (fish_to_blend, layer, blend_layer, samples);
|
|
}
|
|
|
|
blend_func (blend_in, blend_layer, blend_out, samples);
|
|
|
|
composite_in = blend_in;
|
|
composite_layer = blend_layer;
|
|
|
|
if (fish_to_composite)
|
|
{
|
|
if (composite_space == GIMP_LAYER_COLOR_SPACE_RGB_LINEAR)
|
|
{
|
|
composite_in = in;
|
|
composite_layer = layer;
|
|
}
|
|
else
|
|
{
|
|
if (composite_needs_in_color)
|
|
{
|
|
if (composite_in == in && in != out)
|
|
composite_in = g_alloca (sizeof (gfloat) * 4 * samples);
|
|
|
|
babl_process (fish_to_composite,
|
|
blend_in, composite_in, samples);
|
|
}
|
|
|
|
if (composite_needs_layer_color)
|
|
{
|
|
if (composite_layer == layer)
|
|
composite_layer = g_alloca (sizeof (gfloat) * 4 * samples);
|
|
|
|
babl_process (fish_to_composite,
|
|
blend_layer, composite_layer, samples);
|
|
}
|
|
}
|
|
|
|
babl_process (fish_to_composite, blend_out, blend_out, samples);
|
|
}
|
|
|
|
switch (composite_mode)
|
|
{
|
|
case GIMP_LAYER_COMPOSITE_SRC_ATOP:
|
|
default:
|
|
compfun_src_atop_dispatch (composite_in, blend_out, NULL, mask, opacity, out, samples);
|
|
break;
|
|
|
|
case GIMP_LAYER_COMPOSITE_SRC_OVER:
|
|
compfun_src_over_dispatch (composite_in, composite_layer, blend_out, mask, opacity, out, samples);
|
|
break;
|
|
|
|
case GIMP_LAYER_COMPOSITE_DST_ATOP:
|
|
compfun_dst_atop_dispatch (composite_in, composite_layer, blend_out, mask, opacity, out, samples);
|
|
break;
|
|
|
|
case GIMP_LAYER_COMPOSITE_SRC_IN:
|
|
compfun_src_in_dispatch (composite_in, blend_out, NULL, mask, opacity, out, samples);
|
|
break;
|
|
}
|
|
|
|
if (fish_from_composite)
|
|
{
|
|
babl_process (fish_from_composite, out, out, samples);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_screen (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = 1.0f - (1.0f - dest[c]) * (1.0f - src[c]);
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void /* aka linear_dodge */
|
|
blendfun_addition (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] + src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
blendfun_linear_burn (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] + src[c] - 1.0f;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_subtract (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] - src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_multiply (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] * src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_normal (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_burn (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp = 1.0f - (1.0f - dest[c]) / src[c];
|
|
|
|
/* The CLAMP macro is deliberately inlined and written
|
|
* to map comp == NAN (0 / 0) -> 1
|
|
*/
|
|
out[c] = comp < 0 ? 0.0f : comp < 1.0f ? comp : 1.0f;
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_darken_only (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = MIN (dest[c], src[c]);
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_luminance_lighten_only (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
float dest_luminance =
|
|
GIMP_RGB_LUMINANCE(dest[0], dest[1], dest[2]);
|
|
float src_luminance =
|
|
GIMP_RGB_LUMINANCE(src[0], src[1], src[2]);
|
|
|
|
if (dest_luminance >= src_luminance)
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c];
|
|
else
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_luminance_darken_only (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
float dest_luminance =
|
|
GIMP_RGB_LUMINANCE(dest[0], dest[1], dest[2]);
|
|
float src_luminance =
|
|
GIMP_RGB_LUMINANCE(src[0], src[1], src[2]);
|
|
|
|
if (dest_luminance <= src_luminance)
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c];
|
|
else
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = src[c];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_lighten_only (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = MAX (dest[c], src[c]);
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_difference (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
out[c] = dest[c] - src[c];
|
|
|
|
if (out[c] < 0)
|
|
out[c] = -out[c];
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_divide (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp = dest[c] / src[c];
|
|
|
|
/* make infinities(or NaN) correspond to a high number,
|
|
* to get more predictable math, ideally higher than 5.0
|
|
* but it seems like some babl conversions might be
|
|
* acting up then
|
|
*/
|
|
if (!(comp > -42949672.0f && comp < 5.0f))
|
|
comp = 5.0f;
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_dodge (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp = dest[c] / (1.0f - src[c]);
|
|
|
|
comp = MIN (comp, 1.0f);
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_grain_extract (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] - src[c] + 0.5f;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_grain_merge (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
out[c] = dest[c] + src[c] - 0.5f;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_hardlight (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp;
|
|
|
|
if (src[c] > 0.5f)
|
|
{
|
|
comp = (1.0f - dest[c]) * (1.0f - (src[c] - 0.5f) * 2.0f);
|
|
comp = MIN (1 - comp, 1);
|
|
}
|
|
else
|
|
{
|
|
comp = dest[c] * (src[c] * 2.0f);
|
|
comp = MIN (comp, 1.0f);
|
|
}
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_softlight (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat multiply = dest[c] * src[c];
|
|
gfloat screen = 1.0f - (1.0f - dest[c]) * (1.0f - src[c]);
|
|
gfloat comp = (1.0f - dest[c]) * multiply + dest[c] * screen;
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_overlay (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp;
|
|
|
|
if (dest[c] < 0.5f)
|
|
{
|
|
comp = 2.0f * dest[c] * src[c];
|
|
}
|
|
else
|
|
{
|
|
comp = 1.0f - 2.0f * (1.0f - src[c]) * (1.0f - dest[c]);
|
|
}
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_hsv_color (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
GimpRGB dest_rgb = { dest[0], dest[1], dest[2] };
|
|
GimpRGB src_rgb = { src[0], src[1], src[2] };
|
|
GimpHSL src_hsl, dest_hsl;
|
|
|
|
gimp_rgb_to_hsl (&dest_rgb, &dest_hsl);
|
|
gimp_rgb_to_hsl (&src_rgb, &src_hsl);
|
|
|
|
dest_hsl.h = src_hsl.h;
|
|
dest_hsl.s = src_hsl.s;
|
|
|
|
gimp_hsl_to_rgb (&dest_hsl, &dest_rgb);
|
|
|
|
out[RED] = dest_rgb.r;
|
|
out[GREEN] = dest_rgb.g;
|
|
out[BLUE] = dest_rgb.b;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_hsv_hue (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
GimpRGB dest_rgb = { dest[0], dest[1], dest[2] };
|
|
GimpRGB src_rgb = { src[0], src[1], src[2] };
|
|
GimpHSV src_hsv, dest_hsv;
|
|
|
|
gimp_rgb_to_hsv (&dest_rgb, &dest_hsv);
|
|
gimp_rgb_to_hsv (&src_rgb, &src_hsv);
|
|
|
|
dest_hsv.h = src_hsv.h;
|
|
gimp_hsv_to_rgb (&dest_hsv, &dest_rgb);
|
|
|
|
out[RED] = dest_rgb.r;
|
|
out[GREEN] = dest_rgb.g;
|
|
out[BLUE] = dest_rgb.b;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_hsv_saturation (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
GimpRGB dest_rgb = { dest[0], dest[1], dest[2] };
|
|
GimpRGB src_rgb = { src[0], src[1], src[2] };
|
|
GimpHSV src_hsv, dest_hsv;
|
|
|
|
gimp_rgb_to_hsv (&dest_rgb, &dest_hsv);
|
|
gimp_rgb_to_hsv (&src_rgb, &src_hsv);
|
|
|
|
dest_hsv.s = src_hsv.s;
|
|
gimp_hsv_to_rgb (&dest_hsv, &dest_rgb);
|
|
|
|
out[RED] = dest_rgb.r;
|
|
out[GREEN] = dest_rgb.g;
|
|
out[BLUE] = dest_rgb.b;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_hsv_value (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
GimpRGB dest_rgb = { dest[0], dest[1], dest[2] };
|
|
GimpRGB src_rgb = { src[0], src[1], src[2] };
|
|
GimpHSV src_hsv, dest_hsv;
|
|
|
|
gimp_rgb_to_hsv (&dest_rgb, &dest_hsv);
|
|
gimp_rgb_to_hsv (&src_rgb, &src_hsv);
|
|
|
|
dest_hsv.v = src_hsv.v;
|
|
gimp_hsv_to_rgb (&dest_hsv, &dest_rgb);
|
|
|
|
out[RED] = dest_rgb.r;
|
|
out[GREEN] = dest_rgb.g;
|
|
out[BLUE] = dest_rgb.b;
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_lch_chroma (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gfloat A1 = dest[1];
|
|
gfloat B1 = dest[2];
|
|
gfloat c1 = hypotf (A1, B1);
|
|
|
|
if (c1 != 0.0f)
|
|
{
|
|
gfloat A2 = src[1];
|
|
gfloat B2 = src[2];
|
|
gfloat c2 = hypotf (A2, B2);
|
|
gfloat A = c2 * A1 / c1;
|
|
gfloat B = c2 * B1 / c1;
|
|
|
|
out[0] = dest[0];
|
|
out[1] = A;
|
|
out[2] = B;
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_lch_color (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
out[0] = dest[0];
|
|
out[1] = src[1];
|
|
out[2] = src[2];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_lch_hue (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gfloat A2 = src[1];
|
|
gfloat B2 = src[2];
|
|
gfloat c2 = hypotf (A2, B2);
|
|
|
|
if (c2 > 0.1f)
|
|
{
|
|
gfloat A1 = dest[1];
|
|
gfloat B1 = dest[2];
|
|
gfloat c1 = hypotf (A1, B1);
|
|
gfloat A = c1 * A2 / c2;
|
|
gfloat B = c1 * B2 / c2;
|
|
|
|
out[0] = dest[0];
|
|
out[1] = A;
|
|
out[2] = B;
|
|
}
|
|
else
|
|
{
|
|
out[0] = dest[0];
|
|
out[1] = dest[1];
|
|
out[2] = dest[2];
|
|
}
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_lch_lightness (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
out[0] = src[0];
|
|
out[1] = dest[1];
|
|
out[2] = dest[2];
|
|
}
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
blendfun_copy (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
gint c;
|
|
for (c = 0; c < 4; c++)
|
|
out[c] = src[c];
|
|
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
/* added according to:
|
|
http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
|
|
static inline void
|
|
blendfun_vivid_light (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp;
|
|
|
|
if (src[c] > 0.5f)
|
|
{
|
|
comp = (1.0f - (1.0f - dest[c]) / (2.0f * (src[c])));
|
|
}
|
|
else
|
|
{
|
|
comp = dest[c] / (1.0f - 2.0f * (src[c] - 0.5));
|
|
}
|
|
comp = MIN (comp, 1.0f);
|
|
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
|
|
/* added according to:
|
|
http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
|
|
static inline void
|
|
blendfun_linear_light (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp;
|
|
if (src[c] > 0.5f)
|
|
{
|
|
comp = dest[c] + 2.0 * (src[c] - 0.5);
|
|
}
|
|
else
|
|
{
|
|
comp = dest[c] + 2.0 * src[c] - 1.0;
|
|
}
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
|
|
/* added according to:
|
|
http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
|
|
static inline void
|
|
blendfun_pin_light (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
gfloat comp;
|
|
if (src[c] > 0.5f)
|
|
{
|
|
comp = MAX(dest[c], 2 * (src[c] - 0.5));
|
|
}
|
|
else
|
|
{
|
|
comp = MIN(dest[c], 2 * src[c]);
|
|
}
|
|
out[c] = comp;
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
blendfun_exclusion (const float *dest,
|
|
const float *src,
|
|
float *out,
|
|
int samples)
|
|
{
|
|
while (samples--)
|
|
{
|
|
if (dest[ALPHA] != 0.0f && src[ALPHA] != 0.0f)
|
|
{
|
|
gint c;
|
|
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
out[c] = 0.5f - 2.0f * (dest[c] - 0.5f) * (src[c] - 0.5f);
|
|
}
|
|
}
|
|
out[ALPHA] = src[ALPHA];
|
|
|
|
out += 4;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
}
|
|
|
|
static inline void dummy_fun(void)
|
|
{
|
|
}
|
|
|
|
static inline GimpBlendFunc gimp_layer_mode_get_blend_fun (GimpLayerMode mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case GIMP_LAYER_MODE_SCREEN_LINEAR:
|
|
case GIMP_LAYER_MODE_SCREEN: return blendfun_screen;
|
|
case GIMP_LAYER_MODE_ADDITION_LINEAR:
|
|
case GIMP_LAYER_MODE_ADDITION: return blendfun_addition;
|
|
case GIMP_LAYER_MODE_SUBTRACT_LINEAR:
|
|
case GIMP_LAYER_MODE_SUBTRACT: return blendfun_subtract;
|
|
case GIMP_LAYER_MODE_MULTIPLY_LINEAR:
|
|
case GIMP_LAYER_MODE_MULTIPLY: return blendfun_multiply;
|
|
case GIMP_LAYER_MODE_NORMAL_LINEAR:
|
|
case GIMP_LAYER_MODE_NORMAL: return blendfun_normal;
|
|
case GIMP_LAYER_MODE_BURN_LINEAR:
|
|
case GIMP_LAYER_MODE_BURN: return blendfun_burn;
|
|
case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
|
|
case GIMP_LAYER_MODE_GRAIN_MERGE: return blendfun_grain_merge;
|
|
case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
|
|
case GIMP_LAYER_MODE_GRAIN_EXTRACT: return blendfun_grain_extract;
|
|
case GIMP_LAYER_MODE_DODGE_LINEAR:
|
|
case GIMP_LAYER_MODE_DODGE: return blendfun_dodge;
|
|
case GIMP_LAYER_MODE_OVERLAY_LINEAR:
|
|
case GIMP_LAYER_MODE_OVERLAY: return blendfun_overlay;
|
|
case GIMP_LAYER_MODE_HSV_COLOR: return blendfun_hsv_color;
|
|
case GIMP_LAYER_MODE_HSV_HUE: return blendfun_hsv_hue;
|
|
case GIMP_LAYER_MODE_HSV_SATURATION: return blendfun_hsv_saturation;
|
|
case GIMP_LAYER_MODE_HSV_VALUE: return blendfun_hsv_value;
|
|
case GIMP_LAYER_MODE_LCH_CHROMA: return blendfun_lch_chroma;
|
|
case GIMP_LAYER_MODE_LCH_COLOR: return blendfun_lch_color;
|
|
case GIMP_LAYER_MODE_LCH_HUE: return blendfun_lch_hue;
|
|
case GIMP_LAYER_MODE_LCH_LIGHTNESS: return blendfun_lch_lightness;
|
|
case GIMP_LAYER_MODE_HARDLIGHT_LINEAR:
|
|
case GIMP_LAYER_MODE_HARDLIGHT: return blendfun_hardlight;
|
|
case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
|
|
case GIMP_LAYER_MODE_SOFTLIGHT: return blendfun_softlight;
|
|
case GIMP_LAYER_MODE_DIVIDE:
|
|
case GIMP_LAYER_MODE_DIVIDE_LINEAR: return blendfun_divide;
|
|
case GIMP_LAYER_MODE_DIFFERENCE_LINEAR:
|
|
case GIMP_LAYER_MODE_DIFFERENCE: return blendfun_difference;
|
|
case GIMP_LAYER_MODE_DARKEN_ONLY: return blendfun_darken_only;
|
|
case GIMP_LAYER_MODE_LIGHTEN_ONLY: return blendfun_lighten_only;
|
|
case GIMP_LAYER_MODE_LUMINANCE_DARKEN_ONLY:
|
|
case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY: return blendfun_luminance_darken_only;
|
|
case GIMP_LAYER_MODE_LUMINANCE_LIGHTEN_ONLY:
|
|
case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY: return blendfun_luminance_lighten_only;
|
|
case GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR:
|
|
case GIMP_LAYER_MODE_VIVID_LIGHT: return blendfun_vivid_light;
|
|
case GIMP_LAYER_MODE_PIN_LIGHT_LINEAR:
|
|
case GIMP_LAYER_MODE_PIN_LIGHT: return blendfun_pin_light;
|
|
case GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR:
|
|
case GIMP_LAYER_MODE_LINEAR_LIGHT: return blendfun_linear_light;
|
|
case GIMP_LAYER_MODE_EXCLUSION_LINEAR:
|
|
case GIMP_LAYER_MODE_EXCLUSION: return blendfun_exclusion;
|
|
case GIMP_LAYER_MODE_LINEAR_BURN_LINEAR:
|
|
case GIMP_LAYER_MODE_LINEAR_BURN: return blendfun_linear_burn;
|
|
|
|
case GIMP_LAYER_MODE_DISSOLVE:
|
|
case GIMP_LAYER_MODE_BEHIND:
|
|
case GIMP_LAYER_MODE_BEHIND_LINEAR:
|
|
case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
|
|
case GIMP_LAYER_MODE_SCREEN_LEGACY:
|
|
case GIMP_LAYER_MODE_OVERLAY_LEGACY:
|
|
case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
|
|
case GIMP_LAYER_MODE_ADDITION_LEGACY:
|
|
case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
|
|
case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
|
|
case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
|
|
case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
|
|
case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
|
|
case GIMP_LAYER_MODE_HSV_COLOR_LEGACY:
|
|
case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
|
|
case GIMP_LAYER_MODE_DIVIDE_LEGACY:
|
|
case GIMP_LAYER_MODE_DODGE_LEGACY:
|
|
case GIMP_LAYER_MODE_BURN_LEGACY:
|
|
case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
|
|
case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
|
|
case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
|
|
case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
|
|
case GIMP_LAYER_MODE_COLOR_ERASE:
|
|
case GIMP_LAYER_MODE_ERASE:
|
|
case GIMP_LAYER_MODE_REPLACE:
|
|
case GIMP_LAYER_MODE_ANTI_ERASE:
|
|
return (void*)dummy_fun;
|
|
}
|
|
return (void*)dummy_fun;
|
|
}
|