[gimp/pippin/linear-is-the-new-black: 10/10] app: add abstraction for separate blend and composite modes, like LCH mods already do



commit 6a964c964ff8df07bd680bf613df00bbb1566da6
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Jan 15 21:11:00 2017 +0100

    app: add abstraction for separate blend and composite modes, like LCH mods already do

 app/operations/layer-modes/gimpoperationaddition.c |   49 +-
 app/operations/layer-modes/gimpoperationburn.c     |   53 +-
 .../layer-modes/gimpoperationdarkenonly.c          |   47 +-
 .../layer-modes/gimpoperationdifference.c          |   48 +-
 app/operations/layer-modes/gimpoperationdivide.c   |   52 +--
 app/operations/layer-modes/gimpoperationdodge.c    |   49 +-
 .../layer-modes/gimpoperationgrainextract.c        |   48 +-
 .../layer-modes/gimpoperationgrainmerge.c          |   47 +-
 .../layer-modes/gimpoperationhardlight.c           |   61 +--
 app/operations/layer-modes/gimpoperationhsvcolor.c |   60 +--
 app/operations/layer-modes/gimpoperationhsvhue.c   |   65 +--
 .../layer-modes/gimpoperationhsvsaturation.c       |   59 +--
 app/operations/layer-modes/gimpoperationhsvvalue.c |   47 +-
 .../layer-modes/gimpoperationlchchroma.c           |   43 +-
 app/operations/layer-modes/gimpoperationlchcolor.c |   48 +-
 app/operations/layer-modes/gimpoperationlchhue.c   |   45 +-
 .../layer-modes/gimpoperationlchlightness.c        |   48 +-
 .../layer-modes/gimpoperationlightenonly.c         |   47 +-
 app/operations/layer-modes/gimpoperationmultiply.c |   46 +-
 .../layer-modes/gimpoperationpointlayermode.c      |    2 +-
 .../layer-modes/gimpoperationpointlayermode.h      |  843 +++++++++++++++++++-
 app/operations/layer-modes/gimpoperationscreen.c   |   50 +-
 app/operations/layer-modes/gimpoperationsubtract.c |   46 +-
 23 files changed, 1031 insertions(+), 872 deletions(-)
---
diff --git a/app/operations/layer-modes/gimpoperationaddition.c 
b/app/operations/layer-modes/gimpoperationaddition.c
index df412f9..922bf24 100644
--- a/app/operations/layer-modes/gimpoperationaddition.c
+++ b/app/operations/layer-modes/gimpoperationaddition.c
@@ -76,7 +76,6 @@ gimp_operation_addition_process (GeglOperation       *operation,
                                  gint                 level)
 {
   gfloat opacity  = GIMP_OPERATION_POINT_LAYER_MODE (operation)->opacity;
-
   return gimp_operation_addition_process_pixels (in_buf, aux_buf, aux2_buf, out_buf, opacity, samples, roi, 
level);
 }
 
@@ -90,46 +89,12 @@ gimp_operation_addition_process_pixels (gfloat              *in,
                                         const GeglRectangle *roi,
                                         gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] + layer[b];
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_addition
+     );
   return TRUE;
 }
+
diff --git a/app/operations/layer-modes/gimpoperationburn.c b/app/operations/layer-modes/gimpoperationburn.c
index d35737e..080a5e6 100644
--- a/app/operations/layer-modes/gimpoperationburn.c
+++ b/app/operations/layer-modes/gimpoperationburn.c
@@ -80,6 +80,7 @@ gimp_operation_burn_process (GeglOperation       *operation,
   return gimp_operation_burn_process_pixels (in_buf, aux_buf, aux2_buf, out_buf, opacity, samples, roi, 
level);
 }
 
+
 gboolean
 gimp_operation_burn_process_pixels (gfloat              *in,
                                     gfloat              *layer,
@@ -90,50 +91,12 @@ gimp_operation_burn_process_pixels (gfloat              *in,
                                     const GeglRectangle *roi,
                                     gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = 1.0 - (1.0 - in[b]) / layer[b];
-              /* The CLAMP macro is deliberately inlined and
-               * written to map comp == NAN (0 / 0) -> 1
-               */
-              comp = comp < 0 ? 0.0 : comp < 1.0 ? comp : 1.0;
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_burn
+     );
   return TRUE;
 }
+
diff --git a/app/operations/layer-modes/gimpoperationdarkenonly.c 
b/app/operations/layer-modes/gimpoperationdarkenonly.c
index 5ce6dca..869c6d8 100644
--- a/app/operations/layer-modes/gimpoperationdarkenonly.c
+++ b/app/operations/layer-modes/gimpoperationdarkenonly.c
@@ -90,46 +90,11 @@ gimp_operation_darken_only_process_pixels (gfloat              *in,
                                            const GeglRectangle *roi,
                                            gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = MIN (in[b], layer[b]);
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_LINEAR,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_darken_only
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationdifference.c 
b/app/operations/layer-modes/gimpoperationdifference.c
index fc52ca9..d41ccf1 100644
--- a/app/operations/layer-modes/gimpoperationdifference.c
+++ b/app/operations/layer-modes/gimpoperationdifference.c
@@ -90,47 +90,11 @@ gimp_operation_difference_process_pixels (gfloat              *in,
                                           const GeglRectangle *roi,
                                           gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] - layer[b];
-              comp = (comp < 0) ? -comp : comp;
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_LINEAR,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_difference
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationdivide.c 
b/app/operations/layer-modes/gimpoperationdivide.c
index d56534d..4bb47f7 100644
--- a/app/operations/layer-modes/gimpoperationdivide.c
+++ b/app/operations/layer-modes/gimpoperationdivide.c
@@ -90,51 +90,11 @@ gimp_operation_divide_process_pixels (gfloat              *in,
                                       const GeglRectangle *roi,
                                       gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] / layer[b];
-
-              /* make infitinities(or NaN) correspond to a really high number,
-               * to get more predictable math */
-              if (!(comp > -4294967296.0f && comp < 4294967296.0f))
-                comp = 4294967296.0f;
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_divide
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationdodge.c b/app/operations/layer-modes/gimpoperationdodge.c
index 57488ff..8c5da9b 100644
--- a/app/operations/layer-modes/gimpoperationdodge.c
+++ b/app/operations/layer-modes/gimpoperationdodge.c
@@ -90,48 +90,11 @@ gimp_operation_dodge_process_pixels (gfloat              *in,
                                      const GeglRectangle *roi,
                                      gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] / (1.0 - layer[b]);
-              comp = MIN (comp, 1.0);
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_dodge
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationgrainextract.c 
b/app/operations/layer-modes/gimpoperationgrainextract.c
index a298f99..36539e3 100644
--- a/app/operations/layer-modes/gimpoperationgrainextract.c
+++ b/app/operations/layer-modes/gimpoperationgrainextract.c
@@ -90,47 +90,11 @@ gimp_operation_grain_extract_process_pixels (gfloat              *in,
                                              const GeglRectangle *roi,
                                              gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] - layer[b] + 0.5;
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_grain_extract
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationgrainmerge.c 
b/app/operations/layer-modes/gimpoperationgrainmerge.c
index f822225..1c079b5 100644
--- a/app/operations/layer-modes/gimpoperationgrainmerge.c
+++ b/app/operations/layer-modes/gimpoperationgrainmerge.c
@@ -90,46 +90,11 @@ gimp_operation_grain_merge_process_pixels (gfloat              *in,
                                            const GeglRectangle *roi,
                                            gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] + layer[b] - 0.5;
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask ++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_grain_merge
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationhardlight.c 
b/app/operations/layer-modes/gimpoperationhardlight.c
index 34b8db2..a1f83b4 100644
--- a/app/operations/layer-modes/gimpoperationhardlight.c
+++ b/app/operations/layer-modes/gimpoperationhardlight.c
@@ -89,60 +89,11 @@ gimp_operation_hardlight_process_pixels (gfloat              *in,
                                          const GeglRectangle *roi,
                                          gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha, new_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      new_alpha = in[ALPHA] + (1.0 - in[ALPHA]) * comp_alpha;
-
-      if (comp_alpha && new_alpha)
-        {
-          gfloat ratio = comp_alpha / new_alpha;
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp;
-
-              if (layer[b] > 0.5)
-                {
-                  comp = (1.0 - in[b]) * (1.0 - (layer[b] - 0.5) * 2.0);
-                  comp = MIN (1 - comp, 1);
-                }
-              else
-                {
-                  comp = in[b] * (layer[b] * 2.0);
-                  comp = MIN (comp, 1.0);
-                }
-
-              out[b] = comp * ratio + in[b] * (1.0 - ratio);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask ++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_hardlight
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationhsvcolor.c 
b/app/operations/layer-modes/gimpoperationhsvcolor.c
index 756bcb9..6c9b0f9 100644
--- a/app/operations/layer-modes/gimpoperationhsvcolor.c
+++ b/app/operations/layer-modes/gimpoperationhsvcolor.c
@@ -94,59 +94,11 @@ gimp_operation_hsv_color_process_pixels (gfloat              *in,
                                          const GeglRectangle *roi,
                                          gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      GimpHSL layer_hsl, out_hsl;
-      GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
-      GimpRGB out_rgb   = {in[0], in[1], in[2]};
-      gfloat  comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha)
-        {
-          gint   b;
-          gfloat out_tmp[3];
-
-          gimp_rgb_to_hsl (&layer_rgb, &layer_hsl);
-          gimp_rgb_to_hsl (&out_rgb, &out_hsl);
-
-          out_hsl.h = layer_hsl.h;
-          out_hsl.s = layer_hsl.s;
-          gimp_hsl_to_rgb (&out_hsl, &out_rgb);
-
-          out_tmp[0] = out_rgb.r;
-          out_tmp[1] = out_rgb.g;
-          out_tmp[2] = out_rgb.b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = out_tmp[b] * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_hsv_color
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationhsvhue.c 
b/app/operations/layer-modes/gimpoperationhsvhue.c
index e5b2595..da18bd2 100644
--- a/app/operations/layer-modes/gimpoperationhsvhue.c
+++ b/app/operations/layer-modes/gimpoperationhsvhue.c
@@ -94,64 +94,11 @@ gimp_operation_hsv_hue_process_pixels (gfloat              *in,
                                        const GeglRectangle *roi,
                                        gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      GimpHSV layer_hsv, out_hsv;
-      GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
-      GimpRGB out_rgb   = {in[0], in[1], in[2]};
-      gfloat  comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-          gfloat out_tmp[3];
-
-          gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
-          gimp_rgb_to_hsv (&out_rgb, &out_hsv);
-
-          /*  Composition should have no effect if saturation is zero.
-           *  otherwise, black would be painted red (see bug #123296).
-           */
-          if (layer_hsv.s)
-            {
-              out_hsv.h = layer_hsv.h;
-            }
-          gimp_hsv_to_rgb (&out_hsv, &out_rgb);
-
-          out_tmp[0] = out_rgb.r;
-          out_tmp[1] = out_rgb.g;
-          out_tmp[2] = out_rgb.b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = out_tmp[b] * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_hsv_hue
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationhsvsaturation.c 
b/app/operations/layer-modes/gimpoperationhsvsaturation.c
index 88e6ddb..24a18db 100644
--- a/app/operations/layer-modes/gimpoperationhsvsaturation.c
+++ b/app/operations/layer-modes/gimpoperationhsvsaturation.c
@@ -94,58 +94,11 @@ gimp_operation_hsv_saturation_process_pixels (gfloat              *in,
                                               const GeglRectangle *roi,
                                               gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      GimpHSV layer_hsv, out_hsv;
-      GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
-      GimpRGB out_rgb   = {in[0], in[1], in[2]};
-      gfloat  comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-          gfloat out_tmp[3];
-
-          gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
-          gimp_rgb_to_hsv (&out_rgb, &out_hsv);
-
-          out_hsv.s = layer_hsv.s;
-          gimp_hsv_to_rgb (&out_hsv, &out_rgb);
-
-          out_tmp[0] = out_rgb.r;
-          out_tmp[1] = out_rgb.g;
-          out_tmp[2] = out_rgb.b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = out_tmp[b] * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_hsv_saturation
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationhsvvalue.c 
b/app/operations/layer-modes/gimpoperationhsvvalue.c
index 4d1c2c3..87276eb 100644
--- a/app/operations/layer-modes/gimpoperationhsvvalue.c
+++ b/app/operations/layer-modes/gimpoperationhsvvalue.c
@@ -94,46 +94,11 @@ gimp_operation_hsv_value_process_pixels (gfloat              *in,
                                          const GeglRectangle *roi,
                                          gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      GimpHSV layer_hsv, out_hsv;
-      GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
-      GimpRGB out_rgb   = {in[0], in[1], in[2]};
-      gfloat  comp_alpha = layer[ALPHA] * opacity;
-
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
-          gimp_rgb_to_hsv (&out_rgb, &out_hsv);
-
-          out_hsv.v = layer_hsv.v;
-          gimp_hsv_to_rgb (&out_hsv, &out_rgb);
-
-          out[RED]   = out_rgb.r * comp_alpha + in[RED]   * (1.0 - comp_alpha);
-          out[GREEN] = out_rgb.g * comp_alpha + in[GREEN] * (1.0 - comp_alpha);
-          out[BLUE]  = out_rgb.b * comp_alpha + in[BLUE]  * (1.0 - comp_alpha);
-        }
-      else
-        {
-          out[RED]   = in[RED];
-          out[GREEN] = in[GREEN];
-          out[BLUE]  = in[BLUE];
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_hsv_value
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationlchchroma.c 
b/app/operations/layer-modes/gimpoperationlchchroma.c
index 5a08911..71ce2dd 100644
--- a/app/operations/layer-modes/gimpoperationlchchroma.c
+++ b/app/operations/layer-modes/gimpoperationlchchroma.c
@@ -122,30 +122,7 @@ chroma_pre_process (const Babl   *from_fish,
   babl_process (to_fish, out, out, samples);
 }
 
-gboolean
-gimp_operation_lch_chroma_process_pixels_linear (gfloat              *in,
-                                                 gfloat              *layer,
-                                                 gfloat              *mask,
-                                                 gfloat              *out,
-                                                 gfloat               opacity,
-                                                 glong                samples,
-                                                 const GeglRectangle *roi,
-                                                 gint                 level)
-{
-  static const Babl *from_fish;
-  static const Babl *to_fish;
-  
-  if (!from_fish)
-    from_fish = babl_fish ("RGBA float", "CIE Lab alpha float");
-  if (!to_fish)
-     to_fish = babl_fish ("CIE Lab alpha float", "RGBA float");
-
-  chroma_pre_process (from_fish, to_fish, in, layer, out, samples);
-  gimp_operation_layer_composite (in, layer, mask, out, opacity, samples);
-
-  return TRUE;
-}
-
+/* XXX: this and pre_process should be removed */
 gboolean
 gimp_operation_lch_chroma_process_pixels (gfloat              *in,
                                           gfloat              *layer,
@@ -170,3 +147,21 @@ gimp_operation_lch_chroma_process_pixels (gfloat              *in,
   return TRUE;
 }
 
+gboolean
+gimp_operation_lch_chroma_process_pixels_linear (gfloat              *in,
+                                                 gfloat              *layer,
+                                                 gfloat              *mask,
+                                                 gfloat              *out,
+                                                 gfloat               opacity,
+                                                 glong                samples,
+                                                 const GeglRectangle *roi,
+                                                 gint                 level)
+{
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_LAB,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_lch_chroma
+     );
+  return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationlchcolor.c 
b/app/operations/layer-modes/gimpoperationlchcolor.c
index 05badeb..004d4b4 100644
--- a/app/operations/layer-modes/gimpoperationlchcolor.c
+++ b/app/operations/layer-modes/gimpoperationlchcolor.c
@@ -112,33 +112,7 @@ color_pre_process (const Babl   *from_fish_la,
   babl_process (to_fish, out, out, samples);
 }
 
-gboolean
-gimp_operation_lch_color_process_pixels_linear (gfloat              *in,
-                                                gfloat              *layer,
-                                                gfloat              *mask,
-                                                gfloat              *out,
-                                                gfloat               opacity,
-                                                glong                samples,
-                                                const GeglRectangle *roi,
-                                                gint                 level)
-{
-  static const Babl *from_fish_laba = NULL;
-  static const Babl *from_fish_la = NULL;
-  static const Babl *to_fish = NULL;
-  
-  if (!from_fish_laba)
-    from_fish_laba  = babl_fish ("RGBA float", "CIE Lab alpha float");
-  if (!from_fish_la)
-    from_fish_la =  babl_fish ("RGBA float", "CIE L alpha float");
-  if (!to_fish)
-     to_fish = babl_fish ("CIE Lab alpha float", "RGBA float");
-
-  color_pre_process (from_fish_la, from_fish_laba, to_fish, in, layer, out, samples);
-  gimp_operation_layer_composite (in, layer, mask, out, opacity, samples);
-
-  return TRUE;
-}
-
+/* XXX: should be removed along with the pre_process fun */
 gboolean
 gimp_operation_lch_color_process_pixels (gfloat              *in,
                                          gfloat              *layer,
@@ -165,3 +139,23 @@ gimp_operation_lch_color_process_pixels (gfloat              *in,
 
   return TRUE;
 }
+
+
+gboolean
+gimp_operation_lch_color_process_pixels_linear (gfloat              *in,
+                                                gfloat              *layer,
+                                                gfloat              *mask,
+                                                gfloat              *out,
+                                                gfloat               opacity,
+                                                glong                samples,
+                                                const GeglRectangle *roi,
+                                                gint                 level)
+{
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_LAB,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_lch_color
+     );
+  return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationlchhue.c 
b/app/operations/layer-modes/gimpoperationlchhue.c
index 38265f6..36ef390 100644
--- a/app/operations/layer-modes/gimpoperationlchhue.c
+++ b/app/operations/layer-modes/gimpoperationlchhue.c
@@ -123,30 +123,7 @@ hue_pre_process (const Babl   *from_fish,
   babl_process (to_fish, out, out, samples);
 }
 
-gboolean
-gimp_operation_lch_hue_process_pixels_linear (gfloat              *in,
-                                              gfloat              *layer,
-                                              gfloat              *mask,
-                                              gfloat              *out,
-                                              gfloat               opacity,
-                                              glong                samples,
-                                              const GeglRectangle *roi,
-                                              gint                 level)
-{
-  static const Babl *from_fish = NULL;
-  static const Babl *to_fish = NULL;
-  
-  if (!from_fish)
-    from_fish = babl_fish ("RGBA float", "CIE Lab alpha float");
-  if (!to_fish)
-     to_fish = babl_fish ("CIE Lab alpha float", "RGBA float");
-
-  hue_pre_process (from_fish, to_fish, in, layer, out, samples);
-  gimp_operation_layer_composite (in, layer, mask, out, opacity, samples);
-
-  return TRUE;
-}
-
+/* XXX: this should be removed along with _pre_process */
 gboolean
 gimp_operation_lch_hue_process_pixels (gfloat              *in,
                                        gfloat              *layer,
@@ -170,3 +147,23 @@ gimp_operation_lch_hue_process_pixels (gfloat              *in,
 
   return TRUE;
 }
+
+
+gboolean
+gimp_operation_lch_hue_process_pixels_linear (gfloat              *in,
+                                              gfloat              *layer,
+                                              gfloat              *mask,
+                                              gfloat              *out,
+                                              gfloat               opacity,
+                                              glong                samples,
+                                              const GeglRectangle *roi,
+                                              gint                 level)
+{
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_LAB,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_lch_hue
+     );
+  return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationlchlightness.c 
b/app/operations/layer-modes/gimpoperationlchlightness.c
index f452352..b31c9a3 100644
--- a/app/operations/layer-modes/gimpoperationlchlightness.c
+++ b/app/operations/layer-modes/gimpoperationlchlightness.c
@@ -108,33 +108,7 @@ lightness_pre_process (const Babl   *from_fish_la,
   babl_process (to_fish, out, out, samples);
 }
 
-gboolean
-gimp_operation_lch_lightness_process_pixels_linear (gfloat              *in,
-                                                    gfloat              *layer,
-                                                    gfloat              *mask,
-                                                    gfloat              *out,
-                                                    gfloat               opacity,
-                                                    glong                samples,
-                                                    const GeglRectangle *roi,
-                                                    gint                 level)
-{
-  static const Babl *from_fish_laba = NULL;
-  static const Babl *from_fish_la = NULL;
-  static const Babl *to_fish = NULL;
-  
-  if (!from_fish_laba)
-    from_fish_laba  = babl_fish ("RGBA float", "CIE Lab alpha float");
-  if (!from_fish_la)
-    from_fish_la =  babl_fish ("RGBA float", "CIE L alpha float");
-  if (!to_fish)
-     to_fish = babl_fish ("CIE Lab alpha float", "RGBA float");
-
-  lightness_pre_process (from_fish_la, from_fish_laba, to_fish, in, layer, out, samples);
-  gimp_operation_layer_composite (in, layer, mask, out, opacity, samples);
-
-  return TRUE;
-}
-
+/* XXX: this should be remove along with _pre_process */
 gboolean
 gimp_operation_lch_lightness_process_pixels (gfloat              *in,
                                              gfloat              *layer,
@@ -162,3 +136,23 @@ gimp_operation_lch_lightness_process_pixels (gfloat              *in,
 
   return TRUE;
 }
+
+
+gboolean
+gimp_operation_lch_lightness_process_pixels_linear (gfloat              *in,
+                                                    gfloat              *layer,
+                                                    gfloat              *mask,
+                                                    gfloat              *out,
+                                                    gfloat               opacity,
+                                                    glong                samples,
+                                                    const GeglRectangle *roi,
+                                                    gint                 level)
+{
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_LAB,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_lch_lightness
+     );
+  return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationlightenonly.c 
b/app/operations/layer-modes/gimpoperationlightenonly.c
index a0fff11..0f101ef 100644
--- a/app/operations/layer-modes/gimpoperationlightenonly.c
+++ b/app/operations/layer-modes/gimpoperationlightenonly.c
@@ -90,46 +90,11 @@ gimp_operation_lighten_only_process_pixels (gfloat              *in,
                                             const GeglRectangle *roi,
                                             gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = MAX (layer[b], in[b]);
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_LINEAR,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_lighten_only
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationmultiply.c 
b/app/operations/layer-modes/gimpoperationmultiply.c
index 4c77376..dcd5a68 100644
--- a/app/operations/layer-modes/gimpoperationmultiply.c
+++ b/app/operations/layer-modes/gimpoperationmultiply.c
@@ -90,45 +90,11 @@ gimp_operation_multiply_process_pixels (gfloat              *in,
                                         const GeglRectangle *roi,
                                         gint                 level)
 {
-  const gboolean  has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = layer[b] * in[b];
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_multiply
+     );
   return TRUE;
 }
diff --git a/app/operations/layer-modes/gimpoperationpointlayermode.c 
b/app/operations/layer-modes/gimpoperationpointlayermode.c
index 3b19cc8..94c0e1b 100644
--- a/app/operations/layer-modes/gimpoperationpointlayermode.c
+++ b/app/operations/layer-modes/gimpoperationpointlayermode.c
@@ -21,8 +21,8 @@
 
 #include "config.h"
 
-#include <cairo.h>
 #include <gegl-plugin.h>
+#include <cairo.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 #include "libgimpcolor/gimpcolor.h"
diff --git a/app/operations/layer-modes/gimpoperationpointlayermode.h 
b/app/operations/layer-modes/gimpoperationpointlayermode.h
index af0a6e0..5bbfadb 100644
--- a/app/operations/layer-modes/gimpoperationpointlayermode.h
+++ b/app/operations/layer-modes/gimpoperationpointlayermode.h
@@ -23,7 +23,11 @@
 
 
 #include <gegl-plugin.h>
+#include <math.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 
+#include "libgimpcolor/gimpcolor.h"
 
 #define GIMP_TYPE_OPERATION_POINT_LAYER_MODE            (gimp_operation_point_layer_mode_get_type ())
 #define GIMP_OPERATION_POINT_LAYER_MODE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_OPERATION_POINT_LAYER_MODE, GimpOperationPointLayerMode))
@@ -48,10 +52,8 @@ struct _GimpOperationPointLayerMode
   gdouble                      opacity;
 };
 
-
 GType   gimp_operation_point_layer_mode_get_type (void) G_GNUC_CONST;
 
-
 static inline void
 gimp_operation_layer_composite (const gfloat *in,
                                 const gfloat *layer,
@@ -88,6 +90,843 @@ gimp_operation_layer_composite (const gfloat *in,
     }
 }
 
+typedef enum {
+  GIMP_LAYER_BLEND_RGB_LINEAR,
+  GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+  GIMP_LAYER_BLEND_LAB,
+} GimpBlendTRC;
+
+typedef enum {
+  GIMP_LAYER_COMPOSITE_SRC_ATOP,
+  GIMP_LAYER_COMPOSITE_SRC_OVER,
+  GIMP_LAYER_COMPOSITE_SRC_IN,
+  GIMP_LAYER_COMPOSITE_DST_ATOP
+} GimpLayerComposite;
+
+static inline void
+gimp_composite_blend (gfloat              *in,
+                      gfloat              *layer,
+                      gfloat              *mask,
+                      gfloat              *out,
+                      gfloat               opacity,
+                      glong                samples,
+                      GimpBlendTRC         blend_trc,
+                      GimpLayerComposite   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;
+ 
+  if (blendfun == NULL)
+  {
+     blend_out = layer;
+  }
+  else if (blend_trc == GIMP_LAYER_BLEND_RGB_LINEAR)
+  {
+     if (in == out)
+       blend_out   = alloca (sizeof (gfloat) * 4 * samples);
+     blendfun (blend_in, blend_layer, blend_out, samples);
+  }
+  else if (blend_trc == GIMP_LAYER_BLEND_RGB_PERCEPTUAL)
+  {
+    static const Babl *fish_to_perceptual = NULL;
+    static const Babl *fish_to_linear = NULL;
+    if (!fish_to_perceptual)
+      fish_to_perceptual = babl_fish("RGBA float", "R'G'B'A float");
+    if (!fish_to_linear)
+      fish_to_linear = babl_fish("R'G'B'A float", "RGBA float");
+ 
+    blend_in    = alloca (sizeof (gfloat) * 4 * samples);
+    blend_layer = alloca (sizeof (gfloat) * 4 * samples);
+    if (in == out)
+      blend_out   = alloca (sizeof (gfloat) * 4 * samples);
+    babl_process (fish_to_perceptual, in, blend_in, samples);
+    babl_process (fish_to_perceptual, layer, blend_layer, samples);
+ 
+    blendfun (blend_in, blend_layer, blend_out, samples);
+ 
+    babl_process (fish_to_linear, blend_out, blend_out, samples);
+  }
+  else if (blend_trc == GIMP_LAYER_BLEND_LAB)
+  {
+    static const Babl *fish_to_lab = NULL;
+    static const Babl *fish_to_rgb = NULL;
+    if (!fish_to_lab)
+      fish_to_lab = babl_fish("RGBA float", "CIE Lab alpha float");
+    if (!fish_to_rgb)
+      fish_to_rgb = babl_fish("CIE Lab alpha float", "RGBA float");
+ 
+    blend_in    = alloca (sizeof (gfloat) * 4 * samples);
+    babl_process (fish_to_lab, in, blend_in, samples);
+    blend_layer = alloca (sizeof (gfloat) * 4 * samples);
+    babl_process (fish_to_lab, layer, blend_layer, samples);
+ 
+    blendfun (blend_in, blend_layer, out, samples);
+ 
+    babl_process (fish_to_rgb, out, out, samples);
+  }
+ 
+  switch (composite_mode)
+  {
+    case GIMP_LAYER_COMPOSITE_SRC_ATOP:
+    default:
+      while (samples--)
+        {
+          gfloat comp_alpha = blend_out[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] = blend_out[b] * comp_alpha + in[b] * (1.0f - comp_alpha);
+            }
+ 
+          out[ALPHA] = in[ALPHA];
+ 
+          in          += 4;
+          out         += 4;
+          blend_out   += 4;
+ 
+          if (mask)
+            mask++;
+        }
+      break;
+    case GIMP_LAYER_COMPOSITE_SRC_OVER:
+      while (samples--)
+        {
+          gfloat new_alpha;
+          gfloat comp_alpha = blend_out[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] = ratio * (in[ALPHA] * (blend_out[b] - layer[b]) + layer[b] - in[b]) + in[b];
+            }
+ 
+          out[ALPHA] = new_alpha;
+ 
+          in          += 4;
+          layer       += 4;
+          out         += 4;
+          blend_out   += 4;
+ 
+          if (mask)
+            mask++;
+        }
+      break;
+    case GIMP_LAYER_COMPOSITE_DST_ATOP:
+      while (samples--)
+        {
+          gfloat comp_alpha = in[ALPHA];
+ 
+          out[ALPHA] = blend_out[ALPHA] * opacity;
+          if (mask)
+            out[ALPHA] *= *mask;
+ 
+          if (comp_alpha == 0.0f)
+            {
+              out[RED]   = layer[RED];
+              out[GREEN] = layer[GREEN];
+              out[BLUE]  = layer[BLUE];
+            }
+            else
+            {
+              gint   b;
+              for (b = RED; b < ALPHA; b++)
+                out[b] = layer[b] * (1.0f -comp_alpha) + blend_out[b] * (comp_alpha);
+            }
+ 
+          in          += 4;
+          layer       += 4;
+          out         += 4;
+          blend_out   += 4;
+ 
+          if (mask)
+            mask++;
+        }
+      break;
+    case GIMP_LAYER_COMPOSITE_SRC_IN:
+      while (samples--)
+        {
+          gfloat new_alpha = in[ALPHA] * blend_out[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
+            {
+              /* nada */
+            }
+          out[ALPHA] = new_alpha;
+          in          += 4;
+          out         += 4;
+          blend_out   += 4;
+ 
+          if (mask)
+            mask++;
+        }
+      break;
+  }
+}
+ 
+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 d2622c9..011ad3f 100644
--- a/app/operations/layer-modes/gimpoperationscreen.c
+++ b/app/operations/layer-modes/gimpoperationscreen.c
@@ -65,6 +65,7 @@ gimp_operation_screen_init (GimpOperationScreen *self)
 {
 }
 
+
 static gboolean
 gimp_operation_screen_process (GeglOperation       *operation,
                                void                *in_buf,
@@ -76,7 +77,6 @@ gimp_operation_screen_process (GeglOperation       *operation,
                                gint                 level)
 {
   gfloat opacity = GIMP_OPERATION_POINT_LAYER_MODE (operation)->opacity;
-
   return gimp_operation_screen_process_pixels (in_buf, aux_buf, aux2_buf, out_buf, opacity, samples, roi, 
level);
 }
 
@@ -90,46 +90,12 @@ gimp_operation_screen_process_pixels (gfloat              *in,
                                       const GeglRectangle *roi,
                                       gint                 level)
 {
-  const gboolean  has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0f)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = 1.0 - (1.0 - in[b]) * (1.0 - layer[b]);
-
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_screen
+     );
   return TRUE;
 }
+
diff --git a/app/operations/layer-modes/gimpoperationsubtract.c 
b/app/operations/layer-modes/gimpoperationsubtract.c
index 6fbc5e4..b97f17c 100644
--- a/app/operations/layer-modes/gimpoperationsubtract.c
+++ b/app/operations/layer-modes/gimpoperationsubtract.c
@@ -90,45 +90,11 @@ gimp_operation_subtract_process_pixels (gfloat              *in,
                                         const GeglRectangle *roi,
                                         gint                 level)
 {
-  const gboolean has_mask = mask != NULL;
-
-  while (samples--)
-    {
-      gfloat comp_alpha;
-
-      comp_alpha = layer[ALPHA] * opacity;
-      if (has_mask)
-        comp_alpha *= *mask;
-
-      if (comp_alpha != 0.0)
-        {
-          gint   b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              gfloat comp = in[b] - layer[b];
-              out[b] = comp * comp_alpha + in[b] * (1.0 - comp_alpha);
-            }
-        }
-      else
-        {
-          gint b;
-
-          for (b = RED; b < ALPHA; b++)
-            {
-              out[b] = in[b];
-            }
-        }
-
-      out[ALPHA] = in[ALPHA];
-
-      in    += 4;
-      layer += 4;
-      out   += 4;
-
-      if (has_mask)
-        mask++;
-    }
-
+  gimp_composite_blend (
+     in, layer, mask, out, opacity, samples,
+     GIMP_LAYER_BLEND_RGB_PERCEPTUAL,
+     GIMP_LAYER_COMPOSITE_SRC_ATOP,
+     blendfun_subtract
+     );
   return TRUE;
 }


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