[gimp/pippin/linear-is-the-new-black: 7/17] app: give gimp_blend_composite() its own file, and use SIMD for over



commit 58a260c2a3a19aca87f16c252c036d260989cc31
Author: Øyvind Kolås <pippin gimp org>
Date:   Wed Jan 18 23:21:03 2017 +0100

    app: give gimp_blend_composite() its own file, and use SIMD for over
    
    The SIMD code re-uses the existing optimized innerloop from the normal layer
    mode.

 app/operations/layer-modes/Makefile.am             |    1 +
 app/operations/layer-modes/gimpblendcomposite.h    |  912 ++++++++++++++++++++
 app/operations/layer-modes/gimpoperationaddition.c |    4 +-
 app/operations/layer-modes/gimpoperationburn.c     |    2 +-
 .../layer-modes/gimpoperationdarkenonly.c          |    1 +
 .../layer-modes/gimpoperationdifference.c          |    1 +
 app/operations/layer-modes/gimpoperationdivide.c   |    1 +
 app/operations/layer-modes/gimpoperationdodge.c    |    1 +
 .../layer-modes/gimpoperationgrainextract.c        |    1 +
 .../layer-modes/gimpoperationgrainmerge.c          |    1 +
 .../layer-modes/gimpoperationhardlight.c           |    1 +
 app/operations/layer-modes/gimpoperationhsvcolor.c |    1 +
 app/operations/layer-modes/gimpoperationhsvhue.c   |    1 +
 .../layer-modes/gimpoperationhsvsaturation.c       |    1 +
 app/operations/layer-modes/gimpoperationhsvvalue.c |    1 +
 .../layer-modes/gimpoperationlchchroma.c           |    1 +
 app/operations/layer-modes/gimpoperationlchcolor.c |    1 +
 app/operations/layer-modes/gimpoperationlchhue.c   |    1 +
 .../layer-modes/gimpoperationlchlightness.c        |    1 +
 .../layer-modes/gimpoperationlightenonly.c         |    1 +
 app/operations/layer-modes/gimpoperationmultiply.c |    1 +
 app/operations/layer-modes/gimpoperationoverlay.c  |    1 +
 .../layer-modes/gimpoperationpointlayermode.h      |  879 +-------------------
 app/operations/layer-modes/gimpoperationscreen.c   |    1 +
 .../layer-modes/gimpoperationsoftlight.c           |    1 +
 app/operations/layer-modes/gimpoperationsubtract.c |    2 +-
 26 files changed, 939 insertions(+), 881 deletions(-)
---
diff --git a/app/operations/layer-modes/Makefile.am b/app/operations/layer-modes/Makefile.am
index 55c3c3f..2f4d531 100644
--- a/app/operations/layer-modes/Makefile.am
+++ b/app/operations/layer-modes/Makefile.am
@@ -22,6 +22,7 @@ libapplayermodes_generic_a_sources = \
        gimpoperationpointlayermode.h   \
        gimplayermodefunctions.c        \
        gimplayermodefunctions.h        \
+       gimpblendcomposite.h            \
        \
        gimpoperationaddition.c         \
        gimpoperationaddition.h         \
diff --git a/app/operations/layer-modes/gimpblendcomposite.h b/app/operations/layer-modes/gimpblendcomposite.h
new file mode 100644
index 0000000..8ae3b10
--- /dev/null
+++ b/app/operations/layer-modes/gimpblendcomposite.h
@@ -0,0 +1,912 @@
+/* GIMP - The GNU Image Manipulation Program
+ * gimpblendcomposite
+ * Copyright (C) 2017 Øyvind Kolås <pippin gimp 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/>.
+ */
+
+#ifndef __GIMP_BLEND_COMPOSITE_H__
+#define __GIMP_BLEND_COMPOSITE_H__
+
+
+#include <gegl-plugin.h>
+#include <math.h>
+#include <alloca.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "../operations-enums.h"
+#include "../operations-types.h"
+#include "libgimpcolor/gimpcolor.h"
+extern const Babl *_gimp_fish_rgba_to_perceptual;
+extern const Babl *_gimp_fish_perceptual_to_rgba;
+extern const Babl *_gimp_fish_perceptual_to_laba;
+extern const Babl *_gimp_fish_rgba_to_laba;
+extern const Babl *_gimp_fish_laba_to_rgba;
+extern const Babl *_gimp_fish_laba_to_perceptual;
+
+static inline void
+compfun_src_atop (gfloat *in,
+                  gfloat *layer,
+                  gfloat *mask,
+                  gfloat  opacity,
+                  gfloat *out,
+                  gint    samples)
+{
+  while (samples--)
+    {
+      gfloat comp_alpha = layer[ALPHA] * opacity;
+      if (mask)
+        comp_alpha *= *mask;
+ 
+      if (comp_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] * comp_alpha + in[b] * (1.0f - comp_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 *mask,
+                  gfloat *out,
+                  gfloat  opacity,
+                  gint    samples)
+{
+  while (samples--)
+    {
+      gfloat new_alpha;
+      gfloat comp_alpha = layer[ALPHA] * opacity;
+      if (mask)
+        comp_alpha *= *mask;
+ 
+      new_alpha = comp_alpha + (1.0f - comp_alpha) * in[ALPHA];
+ 
+      if (comp_alpha == 0.0f || new_alpha == 0.0f)
+        {
+          out[RED]   = in[RED];
+          out[GREEN] = in[GREEN];
+          out[BLUE]  = in[BLUE];
+        }
+        else
+        {
+          gint   b;
+          gfloat ratio = comp_alpha / new_alpha;
+          for (b = RED; b < ALPHA; b++)
+            out[b] = layer[b] * ratio + in[b] * (1.0f - ratio);
+        }
+ 
+      out[ALPHA] = new_alpha;
+ 
+      in    += 4;
+      layer += 4;
+      out   += 4;
+ 
+      if (mask)
+        mask++;
+    }
+}
+
+
+static inline void
+compfun_src_in (gfloat *in,
+                gfloat *layer,
+                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++;
+    }
+}
+
+static inline void
+gimp_composite_blend (gfloat                *in,
+                      gfloat                *layer,
+                      gfloat                *mask,
+                      gfloat                *out,
+                      gfloat                 opacity,
+                      glong                  samples,
+                      GimpLayerBlendTRC      blend_trc,
+                      GimpLayerBlendTRC      composite_trc,
+                      GimpLayerCompositeMode composite_mode,
+                      void (*blendfun) (const float *dst,
+                                        const float *src,
+                                              float *out,
+                                              int samples))
+{
+  gfloat *blend_in    = in;
+  gfloat *blend_layer = layer;
+  gfloat *blend_out   = out;
+
+  const Babl *fish_to_blend       = NULL;
+  const Babl *fish_to_composite   = NULL;
+  const Babl *fish_from_composite = NULL;
+ 
+  switch (blend_trc)
+  {
+    default:
+    case GIMP_LAYER_BLEND_RGB_LINEAR:
+      fish_to_blend   =  NULL;
+      switch (composite_trc)
+      {
+        case GIMP_LAYER_BLEND_LAB:
+          fish_to_composite   = _gimp_fish_rgba_to_laba;
+          fish_from_composite = _gimp_fish_laba_to_rgba;
+        default:
+        case GIMP_LAYER_BLEND_RGB_LINEAR:
+          fish_to_composite   = NULL;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
+          fish_to_composite   = _gimp_fish_rgba_to_perceptual;
+          fish_from_composite = _gimp_fish_perceptual_to_rgba;
+          break;
+      }
+      break;
+    case GIMP_LAYER_BLEND_LAB:
+      fish_to_blend   = _gimp_fish_rgba_to_laba;
+      switch (composite_trc)
+      {
+        case GIMP_LAYER_BLEND_LAB:
+        default:
+          fish_to_composite = NULL;
+          fish_from_composite = _gimp_fish_laba_to_rgba;
+        case GIMP_LAYER_BLEND_RGB_LINEAR:
+          fish_to_composite = _gimp_fish_laba_to_rgba;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
+          fish_to_composite = _gimp_fish_laba_to_perceptual;
+          fish_from_composite = _gimp_fish_perceptual_to_rgba;
+          break;
+      }
+      break;
+    case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
+      fish_to_blend = _gimp_fish_rgba_to_perceptual;
+      switch (composite_trc)
+      {
+        case GIMP_LAYER_BLEND_LAB:
+        default:
+          fish_to_composite = _gimp_fish_perceptual_to_laba;
+          fish_from_composite = NULL;
+        case GIMP_LAYER_BLEND_RGB_LINEAR:
+          fish_to_composite = _gimp_fish_perceptual_to_rgba;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_BLEND_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 = alloca (sizeof (gfloat) * 4 * samples);
+
+  if (fish_to_blend)
+  {
+    blend_in    = alloca (sizeof (gfloat) * 4 * samples);
+    blend_layer = alloca (sizeof (gfloat) * 4 * samples);
+    babl_process (fish_to_blend, in,    blend_in,  samples);
+    babl_process (fish_to_blend, layer, blend_layer,  samples);
+  }
+
+  blendfun (blend_in, blend_layer, blend_out, samples);
+
+  if (fish_to_composite)
+    {
+      babl_process (fish_to_composite, blend_in, blend_in,   samples);
+      babl_process (fish_to_composite, blend_out, blend_out, samples);
+    }
+ 
+  switch (composite_mode)
+  {
+    case GIMP_LAYER_COMPOSITE_SRC_ATOP:
+    default:
+      compfun_src_atop (blend_in, blend_out, mask, opacity, out, samples);
+      break;
+    case GIMP_LAYER_COMPOSITE_SRC_OVER:
+      //compfun_src_over (blend_in, blend_out, mask, opacity, out, samples);
+      gimp_operation_normal_process_pixels (blend_in, blend_out, mask, out, opacity, samples, NULL, 0, 0, 0, 
0);
+      break;
+    case GIMP_LAYER_COMPOSITE_DST_ATOP:
+      if (fish_to_composite)
+        babl_process (fish_to_composite, blend_layer, blend_layer,  samples);
+
+      compfun_src_atop (blend_out, blend_in, mask, opacity, out, samples); /* swapped arguments */
+      break;
+    case GIMP_LAYER_COMPOSITE_SRC_IN:
+      compfun_src_in (blend_in, blend_out, 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 (src[ALPHA] != 0.0f)
+    {
+      int 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
+blendfun_addition (const float *dest,
+                   const float *src,
+                   float       *out,
+                   int          samples)
+{
+  while (samples--)
+  {
+    if (src[ALPHA] != 0.0f)
+    {
+      int 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_subtract (const float *dest,
+                   const float *src,
+                   float       *out,
+                   int          samples)
+{
+  while (samples--)
+  {
+    if (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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_lighten_only (const float *dest,
+                       const float *src,
+                       float       *out,
+                       int          samples)
+{
+  while (samples--)
+  {
+    if (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 (src[ALPHA] != 0.0f)
+    {
+      int 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 - src[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 (src[ALPHA] != 0.0f)
+    {
+      int c;
+      for (c = 0; c < 3; c++)
+      {
+        gfloat comp;
+        if (src[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 (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 (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 (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 (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 (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 (src[ALPHA] != 0.0f)
+    {
+      out[0] = dest[0];
+      out[1] = src[1];
+      out[2] = src[2];
+    }
+    out[3] = src[3];
+    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 (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[3] = src[3];
+    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 (src[ALPHA] != 0.0f)
+    {
+      out[0] = src[0];
+      out[1] = dest[1];
+      out[2] = dest[2];
+    }
+    out[3] = src[3];
+    out  += 4;
+    src  += 4;
+    dest += 4;
+  }
+}
+
+#endif
diff --git a/app/operations/layer-modes/gimpoperationaddition.c 
b/app/operations/layer-modes/gimpoperationaddition.c
index d975b3c..bd0bf7c 100644
--- a/app/operations/layer-modes/gimpoperationaddition.c
+++ b/app/operations/layer-modes/gimpoperationaddition.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationaddition.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_addition_process (GeglOperation       *operation,
@@ -96,7 +97,8 @@ gimp_operation_addition_process_pixels (gfloat                *in,
                                         GimpLayerCompositeMode composite_mode)
 {
   gimp_composite_blend (in, layer, mask, out, opacity, samples,
-                   blend_trc, composite_trc, composite_mode, blendfun_addition);
+                        blend_trc, composite_trc, composite_mode,
+                        blendfun_addition);
   return TRUE;
 }
 
diff --git a/app/operations/layer-modes/gimpoperationburn.c b/app/operations/layer-modes/gimpoperationburn.c
index f77f71e..b73df72 100644
--- a/app/operations/layer-modes/gimpoperationburn.c
+++ b/app/operations/layer-modes/gimpoperationburn.c
@@ -27,7 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationburn.h"
-
+#include "gimpblendcomposite.h"
 
 static gboolean gimp_operation_burn_process (GeglOperation       *operation,
                                              void                *in_buf,
diff --git a/app/operations/layer-modes/gimpoperationdarkenonly.c 
b/app/operations/layer-modes/gimpoperationdarkenonly.c
index 627af49..ff61d54 100644
--- a/app/operations/layer-modes/gimpoperationdarkenonly.c
+++ b/app/operations/layer-modes/gimpoperationdarkenonly.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationdarkenonly.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_darken_only_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationdifference.c 
b/app/operations/layer-modes/gimpoperationdifference.c
index 8244345..58646fb 100644
--- a/app/operations/layer-modes/gimpoperationdifference.c
+++ b/app/operations/layer-modes/gimpoperationdifference.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationdifference.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_difference_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationdivide.c 
b/app/operations/layer-modes/gimpoperationdivide.c
index e57d7c7..5b3795f 100644
--- a/app/operations/layer-modes/gimpoperationdivide.c
+++ b/app/operations/layer-modes/gimpoperationdivide.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationdivide.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_divide_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationdodge.c b/app/operations/layer-modes/gimpoperationdodge.c
index 025eb38..fb29698 100644
--- a/app/operations/layer-modes/gimpoperationdodge.c
+++ b/app/operations/layer-modes/gimpoperationdodge.c
@@ -27,6 +27,7 @@
 #include "operations/operations-types.h"
 
 #include "gimpoperationdodge.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_dodge_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationgrainextract.c 
b/app/operations/layer-modes/gimpoperationgrainextract.c
index 57756fb..c23659d 100644
--- a/app/operations/layer-modes/gimpoperationgrainextract.c
+++ b/app/operations/layer-modes/gimpoperationgrainextract.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationgrainextract.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_grain_extract_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationgrainmerge.c 
b/app/operations/layer-modes/gimpoperationgrainmerge.c
index f15d58e..813742f 100644
--- a/app/operations/layer-modes/gimpoperationgrainmerge.c
+++ b/app/operations/layer-modes/gimpoperationgrainmerge.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationgrainmerge.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_grain_merge_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationhardlight.c 
b/app/operations/layer-modes/gimpoperationhardlight.c
index 6127f1d..0ada97f 100644
--- a/app/operations/layer-modes/gimpoperationhardlight.c
+++ b/app/operations/layer-modes/gimpoperationhardlight.c
@@ -26,6 +26,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationhardlight.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_hardlight_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationhsvcolor.c 
b/app/operations/layer-modes/gimpoperationhsvcolor.c
index 8c4ae94..401be9b 100644
--- a/app/operations/layer-modes/gimpoperationhsvcolor.c
+++ b/app/operations/layer-modes/gimpoperationhsvcolor.c
@@ -31,6 +31,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationhsvcolor.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_hsv_color_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationhsvhue.c 
b/app/operations/layer-modes/gimpoperationhsvhue.c
index aa8a8aa..54865df 100644
--- a/app/operations/layer-modes/gimpoperationhsvhue.c
+++ b/app/operations/layer-modes/gimpoperationhsvhue.c
@@ -31,6 +31,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationhsvhue.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_hsv_hue_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationhsvsaturation.c 
b/app/operations/layer-modes/gimpoperationhsvsaturation.c
index 32ce7f2..35d7b52 100644
--- a/app/operations/layer-modes/gimpoperationhsvsaturation.c
+++ b/app/operations/layer-modes/gimpoperationhsvsaturation.c
@@ -31,6 +31,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationhsvsaturation.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_hsv_saturation_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationhsvvalue.c 
b/app/operations/layer-modes/gimpoperationhsvvalue.c
index f3ec3d1..d37e75a 100644
--- a/app/operations/layer-modes/gimpoperationhsvvalue.c
+++ b/app/operations/layer-modes/gimpoperationhsvvalue.c
@@ -31,6 +31,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationhsvvalue.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_hsv_value_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationlchchroma.c 
b/app/operations/layer-modes/gimpoperationlchchroma.c
index 3e5d5c8..35f206f 100644
--- a/app/operations/layer-modes/gimpoperationlchchroma.c
+++ b/app/operations/layer-modes/gimpoperationlchchroma.c
@@ -26,6 +26,7 @@
 #include <math.h>
 #include "../operations-types.h"
 #include "gimpoperationlchchroma.h"
+#include "gimpblendcomposite.h"
 
 static gboolean gimp_operation_lch_chroma_process (GeglOperation       *operation,
                                                    void                *in_buf,
diff --git a/app/operations/layer-modes/gimpoperationlchcolor.c 
b/app/operations/layer-modes/gimpoperationlchcolor.c
index 013f182..4efc26e 100644
--- a/app/operations/layer-modes/gimpoperationlchcolor.c
+++ b/app/operations/layer-modes/gimpoperationlchcolor.c
@@ -25,6 +25,7 @@
 #include <gegl-plugin.h>
 #include "../operations-types.h"
 #include "gimpoperationlchcolor.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_lch_color_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationlchhue.c 
b/app/operations/layer-modes/gimpoperationlchhue.c
index 543f689..0e89002 100644
--- a/app/operations/layer-modes/gimpoperationlchhue.c
+++ b/app/operations/layer-modes/gimpoperationlchhue.c
@@ -26,6 +26,7 @@
 #include <math.h>
 #include "../operations-types.h"
 #include "gimpoperationlchhue.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_lch_hue_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationlchlightness.c 
b/app/operations/layer-modes/gimpoperationlchlightness.c
index 3ba5a18..abcf698 100644
--- a/app/operations/layer-modes/gimpoperationlchlightness.c
+++ b/app/operations/layer-modes/gimpoperationlchlightness.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationlchlightness.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_lch_lightness_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationlightenonly.c 
b/app/operations/layer-modes/gimpoperationlightenonly.c
index 5273db9..b2c09f1 100644
--- a/app/operations/layer-modes/gimpoperationlightenonly.c
+++ b/app/operations/layer-modes/gimpoperationlightenonly.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationlightenonly.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_lighten_only_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationmultiply.c 
b/app/operations/layer-modes/gimpoperationmultiply.c
index eb0532f..2ae56e1 100644
--- a/app/operations/layer-modes/gimpoperationmultiply.c
+++ b/app/operations/layer-modes/gimpoperationmultiply.c
@@ -27,6 +27,7 @@
 #include "operations/operations-types.h"
 
 #include "gimpoperationmultiply.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_multiply_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationoverlay.c 
b/app/operations/layer-modes/gimpoperationoverlay.c
index e674f0e..b3d551c 100644
--- a/app/operations/layer-modes/gimpoperationoverlay.c
+++ b/app/operations/layer-modes/gimpoperationoverlay.c
@@ -26,6 +26,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationoverlay.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_overlay_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationpointlayermode.h 
b/app/operations/layer-modes/gimpoperationpointlayermode.h
index 5274f9a..93cc9ab 100644
--- a/app/operations/layer-modes/gimpoperationpointlayermode.h
+++ b/app/operations/layer-modes/gimpoperationpointlayermode.h
@@ -59,884 +59,7 @@ struct _GimpOperationPointLayerMode
 
 GType   gimp_operation_point_layer_mode_get_type (void) G_GNUC_CONST;
 
-extern const Babl *_gimp_fish_rgba_to_perceptual;
-extern const Babl *_gimp_fish_perceptual_to_rgba;
-extern const Babl *_gimp_fish_perceptual_to_laba;
-extern const Babl *_gimp_fish_rgba_to_laba;
-extern const Babl *_gimp_fish_laba_to_rgba;
-extern const Babl *_gimp_fish_laba_to_perceptual;
+#include "gimpoperationnormal.h"
 
-static inline void
-compfun_src_atop (gfloat *in,
-                  gfloat *layer,
-                  gfloat *mask,
-                  gfloat  opacity,
-                  gfloat *out,
-                  gint    samples)
-{
-  while (samples--)
-    {
-      gfloat comp_alpha = layer[ALPHA] * opacity;
-      if (mask)
-        comp_alpha *= *mask;
- 
-      if (comp_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] * comp_alpha + in[b] * (1.0f - comp_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 *mask,
-                  gfloat  opacity,
-                  gfloat *out,
-                  gint    samples)
-{
-  while (samples--)
-    {
-      gfloat new_alpha;
-      gfloat comp_alpha = layer[ALPHA] * opacity;
-      if (mask)
-        comp_alpha *= *mask;
- 
-      new_alpha = comp_alpha + (1.0f - comp_alpha) * in[ALPHA];
- 
-      if (comp_alpha == 0.0f || new_alpha == 0.0f)
-        {
-          out[RED]   = in[RED];
-          out[GREEN] = in[GREEN];
-          out[BLUE]  = in[BLUE];
-        }
-        else
-        {
-          gint   b;
-          gfloat ratio = comp_alpha / new_alpha;
-          for (b = RED; b < ALPHA; b++)
-            out[b] = layer[b] * ratio + in[b] * (1.0f - ratio);
-        }
- 
-      out[ALPHA] = new_alpha;
- 
-      in    += 4;
-      layer += 4;
-      out   += 4;
- 
-      if (mask)
-        mask++;
-    }
-}
-
-
-static inline void
-compfun_src_in (gfloat *in,
-                gfloat *layer,
-                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++;
-    }
-}
-
-static inline void
-gimp_composite_blend (gfloat                *in,
-                      gfloat                *layer,
-                      gfloat                *mask,
-                      gfloat                *out,
-                      gfloat                 opacity,
-                      glong                  samples,
-                      GimpLayerBlendTRC      blend_trc,
-                      GimpLayerBlendTRC      composite_trc,
-                      GimpLayerCompositeMode composite_mode,
-                      void (*blendfun) (const float *dst,
-                                        const float *src,
-                                              float *out,
-                                              int samples))
-{
-  gfloat *blend_in    = in;
-  gfloat *blend_layer = layer;
-  gfloat *blend_out   = out;
-
-  const Babl *fish_to_blend       = NULL;
-  const Babl *fish_to_composite   = NULL;
-  const Babl *fish_from_composite = NULL;
- 
-  switch (blend_trc)
-  {
-    default:
-    case GIMP_LAYER_BLEND_RGB_LINEAR:
-      fish_to_blend   =  NULL;
-      switch (composite_trc)
-      {
-        case GIMP_LAYER_BLEND_LAB:
-          fish_to_composite   = _gimp_fish_rgba_to_laba;
-          fish_from_composite = _gimp_fish_laba_to_rgba;
-        default:
-        case GIMP_LAYER_BLEND_RGB_LINEAR:
-          fish_to_composite   = NULL;
-          fish_from_composite = NULL;
-          break;
-        case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
-          fish_to_composite   = _gimp_fish_rgba_to_perceptual;
-          fish_from_composite = _gimp_fish_perceptual_to_rgba;
-          break;
-      }
-      break;
-    case GIMP_LAYER_BLEND_LAB:
-      fish_to_blend   = _gimp_fish_rgba_to_laba;
-      switch (composite_trc)
-      {
-        case GIMP_LAYER_BLEND_LAB:
-        default:
-          fish_to_composite = NULL;
-          fish_from_composite = _gimp_fish_laba_to_rgba;
-        case GIMP_LAYER_BLEND_RGB_LINEAR:
-          fish_to_composite = _gimp_fish_laba_to_rgba;
-          fish_from_composite = NULL;
-          break;
-        case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
-          fish_to_composite = _gimp_fish_laba_to_perceptual;
-          fish_from_composite = _gimp_fish_perceptual_to_rgba;
-          break;
-      }
-      break;
-    case GIMP_LAYER_BLEND_RGB_PERCEPTUAL:
-      fish_to_blend = _gimp_fish_rgba_to_perceptual;
-      switch (composite_trc)
-      {
-        case GIMP_LAYER_BLEND_LAB:
-        default:
-          fish_to_composite = _gimp_fish_perceptual_to_laba;
-          fish_from_composite = NULL;
-        case GIMP_LAYER_BLEND_RGB_LINEAR:
-          fish_to_composite = _gimp_fish_perceptual_to_rgba;
-          fish_from_composite = NULL;
-          break;
-        case GIMP_LAYER_BLEND_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 = alloca (sizeof (gfloat) * 4 * samples);
-
-  if (fish_to_blend)
-  {
-    blend_in    = alloca (sizeof (gfloat) * 4 * samples);
-    blend_layer = alloca (sizeof (gfloat) * 4 * samples);
-    babl_process (fish_to_blend, in,    blend_in,  samples);
-    babl_process (fish_to_blend, layer, blend_layer,  samples);
-  }
-
-  blendfun (blend_in, blend_layer, blend_out, samples);
-
-  if (fish_to_composite)
-    {
-      babl_process (fish_to_composite, blend_in, blend_in,   samples);
-      babl_process (fish_to_composite, blend_out, blend_out, samples);
-    }
- 
-  switch (composite_mode)
-  {
-    case GIMP_LAYER_COMPOSITE_SRC_ATOP:
-    default:
-      compfun_src_atop (blend_in, blend_out, mask, opacity, out, samples);
-      break;
-    case GIMP_LAYER_COMPOSITE_SRC_OVER:
-      compfun_src_over (blend_in, blend_out, mask, opacity, out, samples);
-      break;
-    case GIMP_LAYER_COMPOSITE_DST_ATOP:
-      if (fish_to_composite)
-        babl_process (fish_to_composite, blend_layer, blend_layer,  samples);
-
-      compfun_src_atop (blend_out, blend_in, mask, opacity, out, samples); /* swapped arguments */
-      break;
-    case GIMP_LAYER_COMPOSITE_SRC_IN:
-      compfun_src_in (blend_in, blend_out, 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 (src[ALPHA] != 0.0f)
-    {
-      int 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
-blendfun_addition (const float *dest,
-                   const float *src,
-                   float       *out,
-                   int          samples)
-{
-  while (samples--)
-  {
-    if (src[ALPHA] != 0.0f)
-    {
-      int 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_subtract (const float *dest,
-                   const float *src,
-                   float       *out,
-                   int          samples)
-{
-  while (samples--)
-  {
-    if (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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_lighten_only (const float *dest,
-                       const float *src,
-                       float       *out,
-                       int          samples)
-{
-  while (samples--)
-  {
-    if (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 (src[ALPHA] != 0.0f)
-    {
-      int 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 - src[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 (src[ALPHA] != 0.0f)
-    {
-      int c;
-      for (c = 0; c < 3; c++)
-      {
-        gfloat comp;
-        if (src[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 (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 (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 (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 (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 (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 (src[ALPHA] != 0.0f)
-    {
-      out[0] = dest[0];
-      out[1] = src[1];
-      out[2] = src[2];
-    }
-    out[3] = src[3];
-    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 (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[3] = src[3];
-    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 (src[ALPHA] != 0.0f)
-    {
-      out[0] = src[0];
-      out[1] = dest[1];
-      out[2] = dest[2];
-    }
-    out[3] = src[3];
-    out  += 4;
-    src  += 4;
-    dest += 4;
-  }
-}
 
 #endif /* __GIMP_OPERATION_POINT_LAYER_MODE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationscreen.c 
b/app/operations/layer-modes/gimpoperationscreen.c
index 90881be..76ac125 100644
--- a/app/operations/layer-modes/gimpoperationscreen.c
+++ b/app/operations/layer-modes/gimpoperationscreen.c
@@ -27,6 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationscreen.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_screen_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationsoftlight.c 
b/app/operations/layer-modes/gimpoperationsoftlight.c
index d35fa8c..9d7f06f 100644
--- a/app/operations/layer-modes/gimpoperationsoftlight.c
+++ b/app/operations/layer-modes/gimpoperationsoftlight.c
@@ -26,6 +26,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationsoftlight.h"
+#include "gimpblendcomposite.h"
 
 
 static gboolean gimp_operation_softlight_process (GeglOperation       *operation,
diff --git a/app/operations/layer-modes/gimpoperationsubtract.c 
b/app/operations/layer-modes/gimpoperationsubtract.c
index e8a1ade..b2b696d 100644
--- a/app/operations/layer-modes/gimpoperationsubtract.c
+++ b/app/operations/layer-modes/gimpoperationsubtract.c
@@ -27,7 +27,7 @@
 #include "../operations-types.h"
 
 #include "gimpoperationsubtract.h"
-
+#include "gimpblendcomposite.h"
 
 static gboolean gimp_operation_subtract_process (GeglOperation       *operation,
                                                  void                *in_buf,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]