[gimp] app: implement non-legacy blend modes in GimpOperationLayerMode



commit d836d94114380c38867b071b14fdd1203f932849
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Jan 22 19:48:49 2017 +0100

    app: implement non-legacy blend modes in GimpOperationLayerMode
    
    For operations needing to override default behavior sub-classes should still be
    used.
    
    This commit also enables pinligh, vividlight and linearlight blend mode modes

 app/core/core-enums.c                              |   20 +
 app/core/core-enums.h                              |   10 +
 app/core/gimp-layer-modes.c                        |  122 +--
 app/operations/gimp-operations.c                   |   48 +-
 app/operations/layer-modes/Makefile.am             |   49 +-
 app/operations/layer-modes/gimpblendcomposite.h    | 1271 ------------------
 .../layer-modes/gimplayermodefunctions.c           |  131 +--
 app/operations/layer-modes/gimpoperationaddition.c |   73 --
 app/operations/layer-modes/gimpoperationaddition.h |   63 -
 app/operations/layer-modes/gimpoperationburn.c     |   72 --
 app/operations/layer-modes/gimpoperationburn.h     |   63 -
 .../layer-modes/gimpoperationdarkenonly.c          |   72 --
 .../layer-modes/gimpoperationdarkenonly.h          |   63 -
 .../layer-modes/gimpoperationdifference.c          |   72 --
 .../layer-modes/gimpoperationdifference.h          |   63 -
 app/operations/layer-modes/gimpoperationdivide.c   |   72 --
 app/operations/layer-modes/gimpoperationdivide.h   |   63 -
 app/operations/layer-modes/gimpoperationdodge.c    |   72 --
 app/operations/layer-modes/gimpoperationdodge.h    |   63 -
 .../layer-modes/gimpoperationgrainextract.c        |   72 --
 .../layer-modes/gimpoperationgrainextract.h        |   63 -
 .../layer-modes/gimpoperationgrainmerge.c          |   72 --
 .../layer-modes/gimpoperationgrainmerge.h          |   63 -
 .../layer-modes/gimpoperationhardlight.c           |   71 -
 .../layer-modes/gimpoperationhardlight.h           |   62 -
 app/operations/layer-modes/gimpoperationhsvcolor.c |   76 --
 app/operations/layer-modes/gimpoperationhsvcolor.h |   63 -
 app/operations/layer-modes/gimpoperationhsvhue.c   |   76 --
 app/operations/layer-modes/gimpoperationhsvhue.h   |   63 -
 .../layer-modes/gimpoperationhsvsaturation.c       |   76 --
 .../layer-modes/gimpoperationhsvsaturation.h       |   63 -
 app/operations/layer-modes/gimpoperationhsvvalue.c |   76 --
 app/operations/layer-modes/gimpoperationhsvvalue.h |   63 -
 .../layer-modes/gimpoperationlayermode.c           | 1348 +++++++++++++++++++-
 .../layer-modes/gimpoperationlayermode.h           |    9 +
 .../layer-modes/gimpoperationlchchroma.c           |   75 --
 .../layer-modes/gimpoperationlchchroma.h           |   64 -
 app/operations/layer-modes/gimpoperationlchcolor.c |   75 --
 app/operations/layer-modes/gimpoperationlchcolor.h |   64 -
 app/operations/layer-modes/gimpoperationlchhue.c   |   75 --
 app/operations/layer-modes/gimpoperationlchhue.h   |   64 -
 .../layer-modes/gimpoperationlchlightness.c        |   75 --
 .../layer-modes/gimpoperationlchlightness.h        |   64 -
 .../layer-modes/gimpoperationlightenonly.c         |   72 --
 .../layer-modes/gimpoperationlightenonly.h         |   63 -
 app/operations/layer-modes/gimpoperationmultiply.c |   72 --
 app/operations/layer-modes/gimpoperationmultiply.h |   63 -
 app/operations/layer-modes/gimpoperationoverlay.c  |   71 -
 app/operations/layer-modes/gimpoperationoverlay.h  |   62 -
 app/operations/layer-modes/gimpoperationscreen.c   |   72 --
 app/operations/layer-modes/gimpoperationscreen.h   |   62 -
 .../layer-modes/gimpoperationsoftlight.c           |   90 --
 .../layer-modes/gimpoperationsoftlight.h           |   62 -
 app/operations/layer-modes/gimpoperationsubtract.c |   72 --
 app/operations/layer-modes/gimpoperationsubtract.h |   63 -
 app/widgets/gimpwidgets-constructors.c             |    9 +-
 libgimp/gimpenums.h                                |   12 +-
 tools/pdbgen/enums.pl                              |   24 +-
 58 files changed, 1460 insertions(+), 4743 deletions(-)
---
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 3e7b530..12e2e80 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -352,6 +352,16 @@ gimp_layer_mode_get_type (void)
     { GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR, "GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR", "grain-extract-linear" },
     { GIMP_LAYER_MODE_GRAIN_MERGE, "GIMP_LAYER_MODE_GRAIN_MERGE", "grain-merge" },
     { GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR, "GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR", "grain-merge-linear" },
+    { GIMP_LAYER_MODE_VIVID_LIGHT, "GIMP_LAYER_MODE_VIVID_LIGHT", "vivid-light" },
+    { GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR, "GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR", "vivid-light-linear" },
+    { GIMP_LAYER_MODE_PIN_LIGHT, "GIMP_LAYER_MODE_PIN_LIGHT", "pin-light" },
+    { GIMP_LAYER_MODE_PIN_LIGHT_LINEAR, "GIMP_LAYER_MODE_PIN_LIGHT_LINEAR", "pin-light-linear" },
+    { GIMP_LAYER_MODE_LINEAR_LIGHT, "GIMP_LAYER_MODE_LINEAR_LIGHT", "linear-light" },
+    { GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR, "GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR", "linear-light-linear" },
+    { GIMP_LAYER_MODE_EXCLUSION, "GIMP_LAYER_MODE_EXCLUSION", "exclusion" },
+    { GIMP_LAYER_MODE_EXCLUSION_LINEAR, "GIMP_LAYER_MODE_EXCLUSION_LINEAR", "exclusion-linear" },
+    { GIMP_LAYER_MODE_LINEAR_BURN, "GIMP_LAYER_MODE_LINEAR_BURN", "linear-burn" },
+    { GIMP_LAYER_MODE_LINEAR_BURN_LINEAR, "GIMP_LAYER_MODE_LINEAR_BURN_LINEAR", "linear-burn-linear" },
     { GIMP_LAYER_MODE_ERASE, "GIMP_LAYER_MODE_ERASE", "erase" },
     { GIMP_LAYER_MODE_REPLACE, "GIMP_LAYER_MODE_REPLACE", "replace" },
     { GIMP_LAYER_MODE_ANTI_ERASE, "GIMP_LAYER_MODE_ANTI_ERASE", "anti-erase" },
@@ -423,6 +433,16 @@ gimp_layer_mode_get_type (void)
     { GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR, NC_("layer-mode", "Grain extract (linear)"), NULL },
     { GIMP_LAYER_MODE_GRAIN_MERGE, NC_("layer-mode", "Grain merge"), NULL },
     { GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR, NC_("layer-mode", "Grain merge (linear)"), NULL },
+    { GIMP_LAYER_MODE_VIVID_LIGHT, NC_("layer-mode", "Vivid light"), NULL },
+    { GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR, NC_("layer-mode", "Vivid light (linear)"), NULL },
+    { GIMP_LAYER_MODE_PIN_LIGHT, NC_("layer-mode", "Pin light"), NULL },
+    { GIMP_LAYER_MODE_PIN_LIGHT_LINEAR, NC_("layer-mode", "Pin light (linear)"), NULL },
+    { GIMP_LAYER_MODE_LINEAR_LIGHT, NC_("layer-mode", "Linear light"), NULL },
+    { GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR, NC_("layer-mode", "Linear light (linear)"), NULL },
+    { GIMP_LAYER_MODE_EXCLUSION, NC_("layer-mode", "Exclusion"), NULL },
+    { GIMP_LAYER_MODE_EXCLUSION_LINEAR, NC_("layer-mode", "Exclusion (linear)"), NULL },
+    { GIMP_LAYER_MODE_LINEAR_BURN, NC_("layer-mode", "Linear light"), NULL },
+    { GIMP_LAYER_MODE_LINEAR_BURN_LINEAR, NC_("layer-mode", "Linear burn (linear)"), NULL },
     { GIMP_LAYER_MODE_ERASE, NC_("layer-mode", "Erase"), NULL },
     { GIMP_LAYER_MODE_REPLACE, NC_("layer-mode", "Replace"), NULL },
     { GIMP_LAYER_MODE_ANTI_ERASE, NC_("layer-mode", "Anti erase"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 4b2ae31..acd103f 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -222,6 +222,16 @@ typedef enum
   GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR,  /*< desc="Grain extract (linear)"   >*/
   GIMP_LAYER_MODE_GRAIN_MERGE,           /*< desc="Grain merge"              >*/
   GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR,    /*< desc="Grain merge (linear)"     >*/
+  GIMP_LAYER_MODE_VIVID_LIGHT,           /*< desc="Vivid light"              >*/
+  GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR,    /*< desc="Vivid light (linear)"     >*/
+  GIMP_LAYER_MODE_PIN_LIGHT,             /*< desc="Pin light"                >*/
+  GIMP_LAYER_MODE_PIN_LIGHT_LINEAR,      /*< desc="Pin light (linear)"       >*/
+  GIMP_LAYER_MODE_LINEAR_LIGHT,          /*< desc="Linear light"             >*/
+  GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR,   /*< desc="Linear light (linear)"    >*/
+  GIMP_LAYER_MODE_EXCLUSION,             /*< desc="Exclusion"                >*/
+  GIMP_LAYER_MODE_EXCLUSION_LINEAR,      /*< desc="Exclusion (linear)"       >*/
+  GIMP_LAYER_MODE_LINEAR_BURN,           /*< desc="Linear light"             >*/
+  GIMP_LAYER_MODE_LINEAR_BURN_LINEAR,    /*< desc="Linear burn (linear)"     >*/
 
   /*  Internal modes, not available to the PDB  */
   GIMP_LAYER_MODE_ERASE      = 1000,     /*< pdb-skip, desc="Erase"          >*/
diff --git a/app/core/gimp-layer-modes.c b/app/core/gimp-layer-modes.c
index b4508b3..488e493 100644
--- a/app/core/gimp-layer-modes.c
+++ b/app/core/gimp-layer-modes.c
@@ -128,6 +128,11 @@ gimp_layer_mode_is_linear (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT:
     case GIMP_LAYER_MODE_GRAIN_MERGE:
+    case GIMP_LAYER_MODE_VIVID_LIGHT:
+    case GIMP_LAYER_MODE_PIN_LIGHT:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT:
+    case GIMP_LAYER_MODE_EXCLUSION:
+    case GIMP_LAYER_MODE_LINEAR_BURN:
       return TRUE;
 
     case GIMP_LAYER_MODE_BEHIND_LINEAR:
@@ -146,6 +151,11 @@ gimp_layer_mode_is_linear (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
+    case GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_PIN_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_EXCLUSION_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_BURN_LINEAR:
       return TRUE;
 
     case GIMP_LAYER_MODE_ERASE:
@@ -158,7 +168,7 @@ gimp_layer_mode_is_linear (GimpLayerMode  mode)
       return TRUE;
   }
 
-  return FALSE;
+  return TRUE;
 }
 
 GimpLayerColorSpace
@@ -227,6 +237,11 @@ gimp_layer_mode_get_blend_space (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT:
     case GIMP_LAYER_MODE_GRAIN_MERGE:
+    case GIMP_LAYER_MODE_VIVID_LIGHT:
+    case GIMP_LAYER_MODE_PIN_LIGHT:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT:
+    case GIMP_LAYER_MODE_EXCLUSION:
+    case GIMP_LAYER_MODE_LINEAR_BURN:
       return GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL;
 
     case GIMP_LAYER_MODE_BEHIND_LINEAR:
@@ -245,6 +260,11 @@ gimp_layer_mode_get_blend_space (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
+    case GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_PIN_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_EXCLUSION_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_BURN_LINEAR:
       return GIMP_LAYER_COLOR_SPACE_RGB_LINEAR;
 
     case GIMP_LAYER_MODE_ERASE:
@@ -328,6 +348,11 @@ gimp_layer_mode_get_composite_mode (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT:
     case GIMP_LAYER_MODE_GRAIN_MERGE:
+    case GIMP_LAYER_MODE_VIVID_LIGHT:
+    case GIMP_LAYER_MODE_PIN_LIGHT:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT:
+    case GIMP_LAYER_MODE_EXCLUSION:
+    case GIMP_LAYER_MODE_LINEAR_BURN:
       return GIMP_LAYER_COMPOSITE_SRC_ATOP;
 
     case GIMP_LAYER_MODE_BEHIND_LINEAR:
@@ -346,6 +371,11 @@ gimp_layer_mode_get_composite_mode (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
     case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
+    case GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_PIN_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR:
+    case GIMP_LAYER_MODE_EXCLUSION_LINEAR:
+    case GIMP_LAYER_MODE_LINEAR_BURN_LINEAR:
       return GIMP_LAYER_COMPOSITE_SRC_ATOP;
 
     case GIMP_LAYER_MODE_ERASE:
@@ -376,147 +406,64 @@ gimp_layer_mode_get_operation (GimpLayerMode  mode)
     case GIMP_LAYER_MODE_MULTIPLY_LEGACY:
       return "gimp:multiply-legacy";
 
-    case GIMP_LAYER_MODE_MULTIPLY:
-    case GIMP_LAYER_MODE_MULTIPLY_LINEAR:
-      return "gimp:multiply";
-
     case GIMP_LAYER_MODE_SCREEN_LEGACY:
       return "gimp:screen-legacy";
 
-    case GIMP_LAYER_MODE_SCREEN:
-    case GIMP_LAYER_MODE_SCREEN_LINEAR:
-      return "gimp:screen";
-
     case GIMP_LAYER_MODE_OVERLAY_LEGACY:
-      return "gimp:softlight-legacy";
 
-    case GIMP_LAYER_MODE_OVERLAY:
-    case GIMP_LAYER_MODE_OVERLAY_LINEAR:
-      return "gimp:overlay";
+      return "gimp:softlight-legacy";
 
     case GIMP_LAYER_MODE_DIFFERENCE_LEGACY:
       return "gimp:difference-legacy";
 
-    case GIMP_LAYER_MODE_DIFFERENCE:
-    case GIMP_LAYER_MODE_DIFFERENCE_LINEAR:
-      return "gimp:difference";
-
     case GIMP_LAYER_MODE_ADDITION_LEGACY:
       return "gimp:addition-legacy";
 
-    case GIMP_LAYER_MODE_ADDITION:
-    case GIMP_LAYER_MODE_ADDITION_LINEAR:
-      return "gimp:addition";
-
     case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
       return "gimp:subtract-legacy";
 
-    case GIMP_LAYER_MODE_SUBTRACT:
-    case GIMP_LAYER_MODE_SUBTRACT_LINEAR:
-      return "gimp:subtract";
-
     case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
       return "gimp:darken-only-legacy";
 
-    case GIMP_LAYER_MODE_DARKEN_ONLY:
-    case GIMP_LAYER_MODE_DARKEN_ONLY_LINEAR:
-      return "gimp:darken-only";
-
     case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
       return "gimp:lighten-only-legacy";
 
-    case GIMP_LAYER_MODE_LIGHTEN_ONLY:
-    case GIMP_LAYER_MODE_LIGHTEN_ONLY_LINEAR:
-      return "gimp:lighten-only";
-
     case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
       return "gimp:hsv-hue-legacy";
 
-    case GIMP_LAYER_MODE_HSV_HUE:
-      return "gimp:hsv-hue";
-
     case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
       return "gimp:hsv-saturation-legacy";
 
-    case GIMP_LAYER_MODE_HSV_SATURATION:
-      return "gimp:hsv-saturation";
-
     case GIMP_LAYER_MODE_HSV_COLOR_LEGACY:
       return "gimp:hsv-color-legacy";
 
-    case GIMP_LAYER_MODE_HSV_COLOR:
-      return "gimp:hsv-color";
-
     case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
       return "gimp:hsv-value-legacy";
 
-    case GIMP_LAYER_MODE_HSV_VALUE:
-      return "gimp:hsv-value";
-
     case GIMP_LAYER_MODE_DIVIDE_LEGACY:
       return "gimp:divide-legacy";
 
-    case GIMP_LAYER_MODE_DIVIDE:
-    case GIMP_LAYER_MODE_DIVIDE_LINEAR:
-      return "gimp:divide";
-
     case GIMP_LAYER_MODE_DODGE_LEGACY:
       return "gimp:dodge-legacy";
 
-    case GIMP_LAYER_MODE_DODGE:
-    case GIMP_LAYER_MODE_DODGE_LINEAR:
-      return "gimp:dodge";
-
     case GIMP_LAYER_MODE_BURN_LEGACY:
       return "gimp:burn-legacy";
 
-    case GIMP_LAYER_MODE_BURN:
-    case GIMP_LAYER_MODE_BURN_LINEAR:
-      return "gimp:burn";
-
     case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
       return "gimp:hardlight-legacy";
 
-    case GIMP_LAYER_MODE_HARDLIGHT:
-    case GIMP_LAYER_MODE_HARDLIGHT_LINEAR:
-      return "gimp:hardlight";
-
     case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
       return "gimp:softlight-legacy";
 
-    case GIMP_LAYER_MODE_SOFTLIGHT:
-    case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
-      return "gimp:softlight";
-
     case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
       return "gimp:grain-extract-legacy";
 
-    case GIMP_LAYER_MODE_GRAIN_EXTRACT:
-    case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
-      return "gimp:grain-extract";
-
     case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
       return "gimp:grain-merge-legacy";
 
-    case GIMP_LAYER_MODE_GRAIN_MERGE:
-    case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
-      return "gimp:grain-merge";
-
     case GIMP_LAYER_MODE_COLOR_ERASE:
       return "gimp:color-erase";
 
-    case GIMP_LAYER_MODE_LCH_HUE:
-      return "gimp:lch-hue";
-
-    case GIMP_LAYER_MODE_LCH_CHROMA:
-      return "gimp:lch-chroma";
-
-    case GIMP_LAYER_MODE_LCH_COLOR:
-      return "gimp:lch-color";
-
-    case GIMP_LAYER_MODE_LCH_LIGHTNESS:
-      return "gimp:lch-lightness";
-
     case GIMP_LAYER_MODE_ERASE:
       return "gimp:erase";
 
@@ -525,7 +472,8 @@ gimp_layer_mode_get_operation (GimpLayerMode  mode)
 
     case GIMP_LAYER_MODE_ANTI_ERASE:
       return "gimp:anti-erase";
+    default:
+      return "gimp:layer-mode";
     }
-
-  return NULL;
+  return "gimp:layer-mode";
 }
diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c
index 4c39444..fc26091 100644
--- a/app/operations/gimp-operations.c
+++ b/app/operations/gimp-operations.c
@@ -84,36 +84,13 @@
 #include "layer-modes-legacy/gimpoperationsoftlightlegacy.h"
 #include "layer-modes-legacy/gimpoperationsubtractlegacy.h"
 
-#include "layer-modes/gimpoperationaddition.h"
 #include "layer-modes/gimpoperationantierase.h"
 #include "layer-modes/gimpoperationbehind.h"
-#include "layer-modes/gimpoperationburn.h"
+#include "layer-modes/gimpoperationerase.h"
 #include "layer-modes/gimpoperationcolorerase.h"
-#include "layer-modes/gimpoperationdarkenonly.h"
-#include "layer-modes/gimpoperationdifference.h"
 #include "layer-modes/gimpoperationdissolve.h"
-#include "layer-modes/gimpoperationdivide.h"
-#include "layer-modes/gimpoperationdodge.h"
-#include "layer-modes/gimpoperationerase.h"
-#include "layer-modes/gimpoperationgrainextract.h"
-#include "layer-modes/gimpoperationgrainmerge.h"
-#include "layer-modes/gimpoperationhardlight.h"
-#include "layer-modes/gimpoperationhsvcolor.h"
-#include "layer-modes/gimpoperationhsvhue.h"
-#include "layer-modes/gimpoperationhsvsaturation.h"
-#include "layer-modes/gimpoperationhsvvalue.h"
-#include "layer-modes/gimpoperationlchchroma.h"
-#include "layer-modes/gimpoperationlchcolor.h"
-#include "layer-modes/gimpoperationlchhue.h"
-#include "layer-modes/gimpoperationlchlightness.h"
-#include "layer-modes/gimpoperationlightenonly.h"
-#include "layer-modes/gimpoperationmultiply.h"
 #include "layer-modes/gimpoperationnormal.h"
-#include "layer-modes/gimpoperationoverlay.h"
 #include "layer-modes/gimpoperationreplace.h"
-#include "layer-modes/gimpoperationscreen.h"
-#include "layer-modes/gimpoperationsoftlight.h"
-#include "layer-modes/gimpoperationsubtract.h"
 
 
 void
@@ -150,48 +127,25 @@ gimp_operations_init (void)
   g_type_class_ref (GIMP_TYPE_OPERATION_NORMAL);
   g_type_class_ref (GIMP_TYPE_OPERATION_DISSOLVE);
   g_type_class_ref (GIMP_TYPE_OPERATION_BEHIND);
-  g_type_class_ref (GIMP_TYPE_OPERATION_MULTIPLY);
   g_type_class_ref (GIMP_TYPE_OPERATION_MULTIPLY_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_SCREEN);
   g_type_class_ref (GIMP_TYPE_OPERATION_SCREEN_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_OVERLAY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_DIFFERENCE);
   g_type_class_ref (GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_ADDITION);
   g_type_class_ref (GIMP_TYPE_OPERATION_ADDITION_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_SUBTRACT);
   g_type_class_ref (GIMP_TYPE_OPERATION_SUBTRACT_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_DARKEN_ONLY);
   g_type_class_ref (GIMP_TYPE_OPERATION_DARKEN_ONLY_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_LIGHTEN_ONLY);
   g_type_class_ref (GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_HSV_HUE);
-  g_type_class_ref (GIMP_TYPE_OPERATION_HSV_SATURATION);
-  g_type_class_ref (GIMP_TYPE_OPERATION_HSV_COLOR);
-  g_type_class_ref (GIMP_TYPE_OPERATION_HSV_VALUE);
   g_type_class_ref (GIMP_TYPE_OPERATION_HSV_HUE_LEGACY);
   g_type_class_ref (GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY);
   g_type_class_ref (GIMP_TYPE_OPERATION_HSV_COLOR_LEGACY);
   g_type_class_ref (GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_DIVIDE);
   g_type_class_ref (GIMP_TYPE_OPERATION_DIVIDE_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_DODGE);
   g_type_class_ref (GIMP_TYPE_OPERATION_DODGE_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_BURN);
   g_type_class_ref (GIMP_TYPE_OPERATION_BURN_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_HARDLIGHT);
   g_type_class_ref (GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_SOFTLIGHT);
   g_type_class_ref (GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_EXTRACT);
   g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY);
-  g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_MERGE);
   g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY);
   g_type_class_ref (GIMP_TYPE_OPERATION_COLOR_ERASE);
-  g_type_class_ref (GIMP_TYPE_OPERATION_LCH_HUE);
-  g_type_class_ref (GIMP_TYPE_OPERATION_LCH_CHROMA);
-  g_type_class_ref (GIMP_TYPE_OPERATION_LCH_COLOR);
-  g_type_class_ref (GIMP_TYPE_OPERATION_LCH_LIGHTNESS);
   g_type_class_ref (GIMP_TYPE_OPERATION_ERASE);
   g_type_class_ref (GIMP_TYPE_OPERATION_REPLACE);
   g_type_class_ref (GIMP_TYPE_OPERATION_ANTI_ERASE);
diff --git a/app/operations/layer-modes/Makefile.am b/app/operations/layer-modes/Makefile.am
index 5559930..cce4753 100644
--- a/app/operations/layer-modes/Makefile.am
+++ b/app/operations/layer-modes/Makefile.am
@@ -24,67 +24,20 @@ libapplayermodes_generic_a_sources = \
        gimplayermodefunctions.h        \
        gimpblendcomposite.h            \
        \
-       gimpoperationaddition.c         \
-       gimpoperationaddition.h         \
        gimpoperationantierase.c        \
        gimpoperationantierase.h        \
        gimpoperationbehind.c           \
        gimpoperationbehind.h           \
-       gimpoperationburn.c             \
-       gimpoperationburn.h             \
        gimpoperationcolorerase.c       \
        gimpoperationcolorerase.h       \
-       gimpoperationdarkenonly.c       \
-       gimpoperationdarkenonly.h       \
-       gimpoperationdifference.c       \
-       gimpoperationdifference.h       \
        gimpoperationdissolve.c         \
        gimpoperationdissolve.h         \
-       gimpoperationdivide.c           \
-       gimpoperationdivide.h           \
-       gimpoperationdodge.c            \
-       gimpoperationdodge.h            \
        gimpoperationerase.c            \
        gimpoperationerase.h            \
-       gimpoperationgrainextract.c     \
-       gimpoperationgrainextract.h     \
-       gimpoperationgrainmerge.c       \
-       gimpoperationgrainmerge.h       \
-       gimpoperationhardlight.c        \
-       gimpoperationhardlight.h        \
-       gimpoperationhsvcolor.c         \
-       gimpoperationhsvcolor.h         \
-       gimpoperationhsvhue.c           \
-       gimpoperationhsvhue.h           \
-       gimpoperationhsvsaturation.c    \
-       gimpoperationhsvsaturation.h    \
-       gimpoperationhsvvalue.c         \
-       gimpoperationhsvvalue.h         \
-       gimpoperationhsvvalue.h         \
-       gimpoperationlchchroma.c        \
-       gimpoperationlchchroma.h        \
-       gimpoperationlchcolor.c         \
-       gimpoperationlchcolor.h         \
-       gimpoperationlchhue.c           \
-       gimpoperationlchhue.h           \
-       gimpoperationlchlightness.c     \
-       gimpoperationlchlightness.h     \
-       gimpoperationlightenonly.c      \
-       gimpoperationlightenonly.h      \
-       gimpoperationmultiply.c         \
-       gimpoperationmultiply.h         \
        gimpoperationnormal.c           \
        gimpoperationnormal.h           \
-       gimpoperationoverlay.c          \
-       gimpoperationoverlay.h          \
        gimpoperationreplace.c          \
-       gimpoperationreplace.h          \
-       gimpoperationscreen.c           \
-       gimpoperationscreen.h           \
-       gimpoperationsoftlight.c        \
-       gimpoperationsoftlight.h        \
-       gimpoperationsubtract.c         \
-       gimpoperationsubtract.h
+       gimpoperationreplace.h
 
 libapplayermodes_sse2_a_sources = \
        gimpoperationnormal-sse2.c
diff --git a/app/operations/layer-modes/gimplayermodefunctions.c 
b/app/operations/layer-modes/gimplayermodefunctions.c
index 3bc190e..2b3ac63 100644
--- a/app/operations/layer-modes/gimplayermodefunctions.c
+++ b/app/operations/layer-modes/gimplayermodefunctions.c
@@ -45,42 +45,19 @@
 #include "operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h"
 #include "operations/layer-modes-legacy/gimpoperationsubtractlegacy.h"
 
-#include "gimpoperationaddition.h"
 #include "gimpoperationantierase.h"
 #include "gimpoperationbehind.h"
-#include "gimpoperationburn.h"
 #include "gimpoperationcolorerase.h"
-#include "gimpoperationdarkenonly.h"
-#include "gimpoperationdifference.h"
 #include "gimpoperationdissolve.h"
-#include "gimpoperationdivide.h"
-#include "gimpoperationdodge.h"
 #include "gimpoperationerase.h"
-#include "gimpoperationgrainextract.h"
-#include "gimpoperationgrainmerge.h"
-#include "gimpoperationhardlight.h"
-#include "gimpoperationhsvcolor.h"
-#include "gimpoperationhsvhue.h"
-#include "gimpoperationhsvsaturation.h"
-#include "gimpoperationhsvvalue.h"
-#include "gimpoperationlchchroma.h"
-#include "gimpoperationlchcolor.h"
-#include "gimpoperationlchhue.h"
-#include "gimpoperationlchlightness.h"
-#include "gimpoperationlightenonly.h"
-#include "gimpoperationmultiply.h"
 #include "gimpoperationnormal.h"
-#include "gimpoperationoverlay.h"
 #include "gimpoperationreplace.h"
-#include "gimpoperationscreen.h"
-#include "gimpoperationsoftlight.h"
-#include "gimpoperationsubtract.h"
 
 
 GimpLayerModeFunc
 gimp_get_layer_mode_function (GimpLayerMode  paint_mode)
 {
-  GimpLayerModeFunc func;
+  GimpLayerModeFunc func = gimp_operation_layer_mode_process_pixels;
 
   switch (paint_mode)
     {
@@ -102,19 +79,10 @@ gimp_get_layer_mode_function (GimpLayerMode  paint_mode)
       func = gimp_operation_multiply_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_MULTIPLY:
-    case GIMP_LAYER_MODE_MULTIPLY_LINEAR:
-      func = gimp_operation_multiply_process;
-      break;
-
     case GIMP_LAYER_MODE_SCREEN_LEGACY:
       func = gimp_operation_screen_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_SCREEN:
-      func = gimp_operation_screen_process;
-      break;
-
     case GIMP_LAYER_MODE_OVERLAY_LEGACY:
       func = gimp_operation_softlight_legacy_process;
       break;
@@ -123,75 +91,34 @@ gimp_get_layer_mode_function (GimpLayerMode  paint_mode)
       func = gimp_operation_difference_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_DIFFERENCE:
-    case GIMP_LAYER_MODE_DIFFERENCE_LINEAR:
-      func = gimp_operation_difference_process;
-      break;
-
     case GIMP_LAYER_MODE_ADDITION_LEGACY:
       func = gimp_operation_addition_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_ADDITION:
-    case GIMP_LAYER_MODE_ADDITION_LINEAR:
-      func = gimp_operation_addition_process;
-      break;
-
     case GIMP_LAYER_MODE_SUBTRACT_LEGACY:
       func = gimp_operation_subtract_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_SUBTRACT:
-    case GIMP_LAYER_MODE_SUBTRACT_LINEAR:
-      func = gimp_operation_subtract_process;
-      break;
-
     case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY:
       func = gimp_operation_darken_only_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_DARKEN_ONLY:
-    case GIMP_LAYER_MODE_DARKEN_ONLY_LINEAR:
-      func = gimp_operation_darken_only_process;
-      break;
-
     case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY:
       func = gimp_operation_lighten_only_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_LIGHTEN_ONLY:
-    case GIMP_LAYER_MODE_LIGHTEN_ONLY_LINEAR:
-      func = gimp_operation_lighten_only_process;
-      break;
-
-    case GIMP_LAYER_MODE_HSV_HUE:
-      func = gimp_operation_hsv_hue_process;
-      break;
-
     case GIMP_LAYER_MODE_HSV_HUE_LEGACY:
       func = gimp_operation_hsv_hue_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_HSV_SATURATION:
-      func = gimp_operation_hsv_saturation_process;
-      break;
-
     case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY:
       func = gimp_operation_hsv_saturation_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_HSV_COLOR:
-      func = gimp_operation_hsv_color_process;
-      break;
-
     case GIMP_LAYER_MODE_HSV_COLOR_LEGACY:
       func = gimp_operation_hsv_color_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_HSV_VALUE:
-      func = gimp_operation_hsv_value_process;
-      break;
-
     case GIMP_LAYER_MODE_HSV_VALUE_LEGACY:
       func = gimp_operation_hsv_value_legacy_process;
       break;
@@ -200,87 +127,34 @@ gimp_get_layer_mode_function (GimpLayerMode  paint_mode)
       func = gimp_operation_divide_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_DIVIDE:
-    case GIMP_LAYER_MODE_DIVIDE_LINEAR:
-      func = gimp_operation_divide_process;
-      break;
-
     case GIMP_LAYER_MODE_DODGE_LEGACY:
       func = gimp_operation_dodge_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_DODGE:
-    case GIMP_LAYER_MODE_DODGE_LINEAR:
-      func = gimp_operation_dodge_process;
-      break;
-
     case GIMP_LAYER_MODE_BURN_LEGACY:
       func = gimp_operation_burn_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_BURN:
-    case GIMP_LAYER_MODE_BURN_LINEAR:
-      func = gimp_operation_burn_process;
-      break;
-
     case GIMP_LAYER_MODE_HARDLIGHT_LEGACY:
       func = gimp_operation_hardlight_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_HARDLIGHT:
-      func = gimp_operation_hardlight_process;
-      break;
-
     case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY:
       func = gimp_operation_softlight_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_SOFTLIGHT:
-      func = gimp_operation_softlight_process;
-      break;
-
     case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY:
       func = gimp_operation_grain_extract_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_GRAIN_EXTRACT:
-    case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
-      func = gimp_operation_grain_extract_process;
-      break;
-
     case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY:
       func = gimp_operation_grain_merge_legacy_process;
       break;
 
-    case GIMP_LAYER_MODE_GRAIN_MERGE:
-    case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
-      func = gimp_operation_grain_merge_process;
-      break;
-
     case GIMP_LAYER_MODE_COLOR_ERASE:
       func = gimp_operation_color_erase_process;
       break;
 
-    case GIMP_LAYER_MODE_OVERLAY:
-      func = gimp_operation_overlay_process;
-      break;
-
-    case GIMP_LAYER_MODE_LCH_HUE:
-      func = gimp_operation_lch_hue_process;
-      break;
-
-    case GIMP_LAYER_MODE_LCH_CHROMA:
-      func = gimp_operation_lch_chroma_process;
-      break;
-
-    case GIMP_LAYER_MODE_LCH_COLOR:
-      func = gimp_operation_lch_color_process;
-      break;
-
-    case GIMP_LAYER_MODE_LCH_LIGHTNESS:
-      func = gimp_operation_lch_lightness_process;
-      break;
-
     case GIMP_LAYER_MODE_ERASE:
       func = gimp_operation_erase_process;
       break;
@@ -294,9 +168,6 @@ gimp_get_layer_mode_function (GimpLayerMode  paint_mode)
       break;
 
     default:
-      g_warning ("No direct function for layer mode (%d), using gimp:normal",
-                 paint_mode);
-      func = gimp_operation_normal_process;
       break;
     }
 
diff --git a/app/operations/layer-modes/gimpoperationlayermode.c 
b/app/operations/layer-modes/gimpoperationlayermode.c
index e339d11..8b44c67 100644
--- a/app/operations/layer-modes/gimpoperationlayermode.c
+++ b/app/operations/layer-modes/gimpoperationlayermode.c
@@ -60,7 +60,6 @@ static gboolean gimp_operation_layer_mode_process      (GeglOperation        *op
                                                         const GeglRectangle  *result,
                                                         gint                  level);
 
-
 G_DEFINE_TYPE (GimpOperationLayerMode, gimp_operation_layer_mode,
                GEGL_TYPE_OPERATION_POINT_COMPOSER3)
 
@@ -78,14 +77,23 @@ const Babl *_gimp_fish_laba_to_perceptual = NULL;
 static void
 gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass)
 {
-  GObjectClass       *object_class    = G_OBJECT_CLASS (klass);
-  GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+  GObjectClass                     *object_class;
+  GeglOperationClass               *operation_class;
+  GeglOperationPointComposer3Class *point_composer3_class;
+
+  object_class          = G_OBJECT_CLASS (klass);
+  operation_class       = GEGL_OPERATION_CLASS (klass);
+  point_composer3_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
+
+  gegl_operation_class_set_keys (operation_class,
+                                 "name", "gimp:layer-mode", NULL);
 
   object_class->set_property = gimp_operation_layer_mode_set_property;
   object_class->get_property = gimp_operation_layer_mode_get_property;
 
-  operation_class->prepare   = gimp_operation_layer_mode_prepare;
-  operation_class->process   = gimp_operation_layer_mode_process;
+  operation_class->prepare       = gimp_operation_layer_mode_prepare;
+  operation_class->process       = gimp_operation_layer_mode_process;
+  point_composer3_class->process = gimp_operation_layer_mode_process_pixels;
 
   g_object_class_install_property (object_class, PROP_LAYER_MODE,
                                    g_param_spec_enum ("layer-mode",
@@ -276,3 +284,1333 @@ gimp_operation_layer_mode_process (GeglOperation        *operation,
                                                        output_prop, result,
                                                        level);
 }
+
+
+static inline GimpBlendFunc gimp_layer_mode_get_blend_fun (GimpLayerMode mode);
+
+static inline void gimp_composite_blend (gpointer       op,
+                                         gfloat        *in,
+                                         gfloat        *layer,
+                                         gfloat        *mask,
+                                         gfloat        *out,
+                                         glong          samples,
+                                         GimpBlendFunc  blend_func);
+gboolean
+gimp_operation_layer_mode_process_pixels (GeglOperation       *operation,
+                                          void                *in,
+                                          void                *layer,
+                                          void                *mask,
+                                          void                *out,
+                                          glong                samples,
+                                          const GeglRectangle *roi,
+                                          gint                 level)
+{
+  GimpOperationLayerMode *layer_mode = (GimpOperationLayerMode*)(operation);
+  gimp_composite_blend (operation, in, layer, mask, out, samples, 
+                        gimp_layer_mode_get_blend_fun (layer_mode->layer_mode));
+  return TRUE;
+}
+
+
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.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 layer_alpha = layer[ALPHA] * opacity;
+
+      if (mask)
+        layer_alpha *= *mask;
+
+      if (layer_alpha == 0.0f)
+        {
+          out[RED]   = in[RED];
+          out[GREEN] = in[GREEN];
+          out[BLUE]  = in[BLUE];
+        }
+      else
+        {
+          gint b;
+
+          for (b = RED; b < ALPHA; b++)
+            out[b] = layer[b] * layer_alpha + in[b] * (1.0f - layer_alpha);
+        }
+
+      out[ALPHA] = in[ALPHA];
+
+      in    += 4;
+      out   += 4;
+      layer += 4;
+
+      if (mask)
+        mask++;
+    }
+}
+
+static inline void
+compfun_src_over (gfloat *in,
+                  gfloat *layer,
+                  gfloat *comp,
+                  gfloat *mask,
+                  gfloat  opacity,
+                  gfloat *out,
+                  gint    samples)
+{
+  while (samples--)
+    {
+      gfloat new_alpha;
+      gfloat layer_alpha = layer[ALPHA] * opacity;
+
+      if (mask)
+        layer_alpha *= *mask;
+
+      new_alpha = layer_alpha + (1.0f - layer_alpha) * in[ALPHA];
+
+      if (layer_alpha == 0.0f || new_alpha == 0.0f)
+        {
+          out[RED]   = in[RED];
+          out[GREEN] = in[GREEN];
+          out[BLUE]  = in[BLUE];
+        }
+      else
+        {
+          gfloat ratio = layer_alpha / new_alpha;
+          gint   b;
+
+          for (b = RED; b < ALPHA; b++)
+            out[b] = ratio * (in[ALPHA] * (comp[b] - layer[b]) + layer[b] - in[b]) + in[b];
+        }
+
+      out[ALPHA] = new_alpha;
+
+      in    += 4;
+      layer += 4;
+      comp  += 4;
+      out   += 4;
+
+      if (mask)
+        mask++;
+    }
+}
+
+static inline void
+compfun_dst_atop (gfloat *in,
+                  gfloat *layer,
+                  gfloat *comp,
+                  gfloat *mask,
+                  gfloat  opacity,
+                  gfloat *out,
+                  gint    samples)
+{
+  while (samples--)
+    {
+      gfloat layer_alpha = layer[ALPHA] * opacity;
+
+      if (mask)
+        layer_alpha *= *mask;
+
+      if (layer_alpha == 0.0f)
+        {
+          out[RED]   = in[RED];
+          out[GREEN] = in[GREEN];
+          out[BLUE]  = in[BLUE];
+        }
+      else
+        {
+          gint b;
+
+          for (b = RED; b < ALPHA; b++)
+            out[b] = comp[b] * in[ALPHA] + layer[b] * (1.0f - in[ALPHA]);
+        }
+
+      out[ALPHA] = layer_alpha;
+
+      in    += 4;
+      layer += 4;
+      comp  += 4;
+      out   += 4;
+
+      if (mask)
+        mask++;
+    }
+}
+
+static inline void
+compfun_src_in (gfloat *in,
+                gfloat *layer,
+                gfloat *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 (gpointer                op,
+                      gfloat                 *in,
+                      gfloat                 *layer,
+                      gfloat                 *mask,
+                      gfloat                 *out,
+                      glong                   samples,
+                      GimpBlendFunc           blend_func)
+{
+  GimpOperationLayerMode *layer_mode     = op;
+  gfloat                  opacity        = layer_mode->opacity;
+  GimpLayerColorSpace     blend_space    = layer_mode->blend_space;
+  GimpLayerColorSpace     composite_space= layer_mode->composite_space;
+  GimpLayerCompositeMode  composite_mode = layer_mode->composite_mode;
+
+  gfloat *blend_in    = in;
+  gfloat *blend_layer = layer;
+  gfloat *blend_out   = out;
+
+  gfloat *composite_in    = NULL;
+  gfloat *composite_layer = NULL;
+
+  gboolean composite_needs_in_color =
+    composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
+    composite_mode == GIMP_LAYER_COMPOSITE_SRC_ATOP;
+  gboolean composite_needs_layer_color =
+    composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
+    composite_mode == GIMP_LAYER_COMPOSITE_DST_ATOP;
+
+  const Babl *fish_to_blend       = NULL;
+  const Babl *fish_to_composite   = NULL;
+  const Babl *fish_from_composite = NULL;
+
+  switch (blend_space)
+    {
+    default:
+    case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
+      fish_to_blend   =  NULL;
+      switch (composite_space)
+        {
+        case GIMP_LAYER_COLOR_SPACE_LAB:
+          fish_to_composite   = _gimp_fish_rgba_to_laba;
+          fish_from_composite = _gimp_fish_laba_to_rgba;
+          break;
+        default:
+        case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
+          fish_to_composite   = NULL;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
+          fish_to_composite   = _gimp_fish_rgba_to_perceptual;
+          fish_from_composite = _gimp_fish_perceptual_to_rgba;
+          break;
+        }
+      break;
+
+    case GIMP_LAYER_COLOR_SPACE_LAB:
+      fish_to_blend   = _gimp_fish_rgba_to_laba;
+      switch (composite_space)
+        {
+        case GIMP_LAYER_COLOR_SPACE_LAB:
+        default:
+          fish_to_composite = NULL;
+          fish_from_composite = _gimp_fish_laba_to_rgba;
+          break;
+        case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
+          fish_to_composite = _gimp_fish_laba_to_rgba;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
+          fish_to_composite = _gimp_fish_laba_to_perceptual;
+          fish_from_composite = _gimp_fish_perceptual_to_rgba;
+          break;
+        }
+      break;
+
+    case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
+      fish_to_blend = _gimp_fish_rgba_to_perceptual;
+      switch (composite_space)
+        {
+        case GIMP_LAYER_COLOR_SPACE_LAB:
+        default:
+          fish_to_composite = _gimp_fish_perceptual_to_laba;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
+          fish_to_composite = _gimp_fish_perceptual_to_rgba;
+          fish_from_composite = NULL;
+          break;
+        case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
+          fish_to_composite = NULL;
+          fish_from_composite = _gimp_fish_perceptual_to_rgba;
+          break;
+        }
+      break;
+    }
+
+  if (in == out) /* in-place detected, avoid clobbering since we need to
+                    read it for the compositing stage  */
+    blend_out = g_alloca (sizeof (gfloat) * 4 * samples);
+
+  if (fish_to_blend)
+    {
+      if (in != out || (composite_needs_in_color &&
+                        composite_space == GIMP_LAYER_COLOR_SPACE_RGB_LINEAR))
+        {
+          /* don't convert input in-place if we're not doing in-place output,
+           * or if we're going to need the original input for compositing.
+           */
+          blend_in = g_alloca (sizeof (gfloat) * 4 * samples);
+        }
+      blend_layer  = g_alloca (sizeof (gfloat) * 4 * samples);
+
+      babl_process (fish_to_blend, in,    blend_in,    samples);
+      babl_process (fish_to_blend, layer, blend_layer, samples);
+    }
+
+  blend_func (blend_in, blend_layer, blend_out, samples);
+
+  composite_in    = blend_in;
+  composite_layer = blend_layer;
+
+  if (fish_to_composite)
+    {
+      if (composite_space == GIMP_LAYER_COLOR_SPACE_RGB_LINEAR)
+        {
+          composite_in    = in;
+          composite_layer = layer;
+        }
+      else
+        {
+          if (composite_needs_in_color)
+            {
+              if (composite_in == in && in != out)
+                composite_in = g_alloca (sizeof (gfloat) * 4 * samples);
+
+              babl_process (fish_to_composite,
+                            blend_in, composite_in, samples);
+            }
+
+          if (composite_needs_layer_color)
+            {
+              if (composite_layer == layer)
+                composite_layer = g_alloca (sizeof (gfloat) * 4 * samples);
+
+              babl_process (fish_to_composite,
+                            blend_layer, composite_layer, samples);
+            }
+        }
+
+      babl_process (fish_to_composite, blend_out, blend_out, samples);
+    }
+
+  switch (composite_mode)
+    {
+    case GIMP_LAYER_COMPOSITE_SRC_ATOP:
+    default:
+      compfun_src_atop (composite_in, blend_out, mask, opacity, out, samples);
+      break;
+
+    case GIMP_LAYER_COMPOSITE_SRC_OVER:
+      compfun_src_over (composite_in, composite_layer, blend_out, mask, opacity, out, samples);
+      break;
+
+    case GIMP_LAYER_COMPOSITE_DST_ATOP:
+      compfun_dst_atop (composite_in, composite_layer, blend_out, mask, opacity, out, samples);
+      break;
+
+    case GIMP_LAYER_COMPOSITE_SRC_IN:
+      compfun_src_in (composite_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)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = 1.0f - (1.0f - dest[c])   * (1.0f - src[c]);
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void /* aka linear_dodge */
+blendfun_addition (const float *dest,
+                   const float *src,
+                   float       *out,
+                   int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] + src[c];
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+
+static inline void 
+blendfun_linear_burn (const float *dest,
+                      const float *src,
+                      float       *out,
+                      int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] + src[c] - 1.0f;
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_subtract (const float *dest,
+                   const float *src,
+                   float       *out,
+                   int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] - src[c];
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_multiply (const float *dest,
+                   const float *src,
+                   float       *out,
+                   int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] * src[c];
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_normal (const float *dest,
+                 const float *src,
+                 float       *out,
+                 int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = src[c];
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_burn (const float *dest,
+               const float *src,
+               float       *out,
+               int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp = 1.0f - (1.0f - dest[c]) / src[c];
+
+              /* The CLAMP macro is deliberately inlined and written
+               * to map comp == NAN (0 / 0) -> 1
+               */
+              out[c] = comp < 0 ? 0.0f : comp < 1.0f ? comp : 1.0f;
+            }
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_darken_only (const float *dest,
+                      const float *src,
+                      float       *out,
+                      int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = MIN (dest[c], src[c]);
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_lighten_only (const float *dest,
+                       const float *src,
+                       float       *out,
+                       int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = MAX (dest[c], src[c]);
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_difference (const float *dest,
+                     const float *src,
+                     float       *out,
+                     int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              out[c] = dest[c] - src[c];
+
+              if (out[c] < 0)
+                out[c] = -out[c];
+            }
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_divide (const float *dest,
+                 const float *src,
+                 float       *out,
+                 int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp = dest[c] / src[c];
+
+              /* make infinities(or NaN) correspond to a high number,
+               * to get more predictable math, ideally higher than 5.0
+               * but it seems like some babl conversions might be
+               * acting up then
+               */
+              if (!(comp > -42949672.0f && comp < 5.0f))
+                comp = 5.0f;
+
+              out[c] = comp;
+            }
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_dodge (const float *dest,
+                const float *src,
+                float       *out,
+                int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp = dest[c] / (1.0f - src[c]);
+
+              comp = MIN (comp, 1.0f);
+
+              out[c] = comp;
+            }
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_grain_extract (const float *dest,
+                        const float *src,
+                        float       *out,
+                        int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] - src[c] + 0.5f;
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_grain_merge (const float *dest,
+                      const float *src,
+                      float       *out,
+                      int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            out[c] = dest[c] + src[c] - 0.5f;
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_hardlight (const float *dest,
+                    const float *src,
+                    float       *out,
+                    int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp;
+
+              if (src[c] > 0.5f)
+                {
+                  comp = (1.0f - dest[c]) * (1.0f - (src[c] - 0.5f) * 2.0f);
+                  comp = MIN (1 - comp, 1);
+                }
+              else
+                {
+                  comp = dest[c] * (src[c] * 2.0f);
+                  comp = MIN (comp, 1.0f);
+                }
+
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_softlight (const float *dest,
+                    const float *src,
+                    float       *out,
+                    int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat multiply = dest[c] * src[c];
+              gfloat screen   = 1.0f - (1.0f - dest[c]) * (1.0f - src[c]);
+              gfloat comp     = (1.0f - dest[c]) * multiply + dest[c] * screen;
+
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_overlay (const float *dest,
+                  const float *src,
+                  float       *out,
+                  int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp;
+
+              if (dest[c] < 0.5f)
+                {
+                  comp = 2.0f * dest[c] * src[c];
+                }
+              else
+                {
+                  comp = 1.0f - 2.0f * (1.0f - src[c]) * (1.0f - dest[c]);
+                }
+
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_hsv_color (const float *dest,
+                    const float *src,
+                    float       *out,
+                    int          samples)
+{
+  while (samples--)
+    {
+      if (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[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+  }
+}
+
+static inline void
+blendfun_lch_hue (const float *dest,
+                  const float *src,
+                  float       *out,
+                  int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gfloat A2 = src[1];
+          gfloat B2 = src[2];
+          gfloat c2 = hypotf (A2, B2);
+
+          if (c2 > 0.1f)
+            {
+              gfloat A1 = dest[1];
+              gfloat B1 = dest[2];
+              gfloat c1 = hypotf (A1, B1);
+              gfloat A  = c1 * A2 / c2;
+              gfloat B  = c1 * B2 / c2;
+
+              out[0] = dest[0];
+              out[1] = A;
+              out[2] = B;
+            }
+          else
+            {
+              out[0] = dest[0];
+              out[1] = dest[1];
+              out[2] = dest[2];
+            }
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_lch_lightness (const float *dest,
+                        const float *src,
+                        float       *out,
+                        int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          out[0] = src[0];
+          out[1] = dest[1];
+          out[2] = dest[2];
+        }
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+
+static inline void
+blendfun_copy (const float *dest,
+               const float *src,
+               float       *out,
+               int          samples)
+{
+  while (samples--)
+    {
+      gint c;
+      for (c = 0; c < 4; c++)
+        out[c] = src[c];
+
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+/* added according to: 
+    http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
+static inline void
+blendfun_vivid_light (const float *dest,
+                      const float *src,
+                      float       *out,
+                      int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp;
+
+              if (src[c] > 0.5f)
+                {
+                  comp = (1.0f - (1.0f - dest[c]) / (2.0f * (src[c])));
+                }
+              else
+                {
+                  comp = dest[c] / (1.0f - 2.0f * (src[c] - 0.5));
+                }
+              comp = MIN (comp, 1.0f);
+
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+
+/* added according to: 
+    http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
+static inline void
+blendfun_linear_light (const float *dest,
+                       const float *src,
+                       float       *out,
+                       int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp;
+              if (src[c] > 0.5f)
+                {
+                  comp = dest[c] + 2.0 * (src[c] - 0.5);
+                }
+              else
+                {
+                  comp = dest[c] + 2.0 * src[c] - 1.0;
+                }
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+
+/* added according to: 
+    http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
+static inline void
+blendfun_pin_light (const float *dest,
+                    const float *src,
+                    float       *out,
+                    int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              gfloat comp;
+              if (src[c] > 0.5f)
+                {
+                  comp = MAX(dest[c], 2 * (src[c] - 0.5));
+                }
+              else
+                {
+                  comp = MIN(dest[c], 2 * src[c]);
+                }
+              out[c] = comp;
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void
+blendfun_exclusion (const float *dest,
+                    const float *src,
+                    float       *out,
+                    int          samples)
+{
+  while (samples--)
+    {
+      if (src[ALPHA] != 0.0f)
+        {
+          gint c;
+
+          for (c = 0; c < 3; c++)
+            {
+              out[c] = 0.5f - 2.0f * (dest[c] - 0.5f) * (src[c] - 0.5f);
+            }
+        }
+      out[ALPHA] = src[ALPHA];
+
+      out  += 4;
+      src  += 4;
+      dest += 4;
+    }
+}
+
+static inline void dummy_fun(void)
+{
+}
+
+static inline GimpBlendFunc gimp_layer_mode_get_blend_fun (GimpLayerMode mode)
+{
+  switch (mode)
+  {
+    case GIMP_LAYER_MODE_SCREEN_LINEAR:
+    case GIMP_LAYER_MODE_SCREEN:         return blendfun_screen;
+    case GIMP_LAYER_MODE_ADDITION_LINEAR:
+    case GIMP_LAYER_MODE_ADDITION:       return blendfun_addition;
+    case GIMP_LAYER_MODE_SUBTRACT_LINEAR:
+    case GIMP_LAYER_MODE_SUBTRACT:       return blendfun_subtract;
+    case GIMP_LAYER_MODE_MULTIPLY_LINEAR:
+    case GIMP_LAYER_MODE_MULTIPLY:       return blendfun_multiply;
+    case GIMP_LAYER_MODE_NORMAL:         return blendfun_normal;
+    case GIMP_LAYER_MODE_BURN_LINEAR:
+    case GIMP_LAYER_MODE_BURN:           return blendfun_burn;
+    case GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR:
+    case GIMP_LAYER_MODE_GRAIN_MERGE:    return blendfun_grain_merge;
+    case GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR:
+    case GIMP_LAYER_MODE_GRAIN_EXTRACT:  return blendfun_grain_merge;
+    case GIMP_LAYER_MODE_DODGE_LINEAR: 
+    case GIMP_LAYER_MODE_DODGE:          return blendfun_dodge;
+    case GIMP_LAYER_MODE_OVERLAY_LINEAR:
+    case GIMP_LAYER_MODE_OVERLAY:        return blendfun_overlay;
+    case GIMP_LAYER_MODE_HSV_COLOR:      return blendfun_hsv_color;
+    case GIMP_LAYER_MODE_HSV_HUE:        return blendfun_hsv_hue;
+    case GIMP_LAYER_MODE_HSV_SATURATION: return blendfun_hsv_saturation;
+    case GIMP_LAYER_MODE_HSV_VALUE:      return blendfun_hsv_value;
+    case GIMP_LAYER_MODE_LCH_CHROMA:     return blendfun_lch_chroma;
+    case GIMP_LAYER_MODE_LCH_COLOR:      return blendfun_lch_color;
+    case GIMP_LAYER_MODE_LCH_HUE:        return blendfun_lch_hue;
+    case GIMP_LAYER_MODE_LCH_LIGHTNESS:  return blendfun_lch_lightness;
+    case GIMP_LAYER_MODE_HARDLIGHT_LINEAR:
+    case GIMP_LAYER_MODE_HARDLIGHT:      return blendfun_hardlight;
+    case GIMP_LAYER_MODE_SOFTLIGHT_LINEAR:
+    case GIMP_LAYER_MODE_SOFTLIGHT:      return blendfun_softlight;
+    case GIMP_LAYER_MODE_DIVIDE:
+    case GIMP_LAYER_MODE_DIVIDE_LINEAR:  return blendfun_divide;
+    case GIMP_LAYER_MODE_DIFFERENCE_LINEAR: 
+    case GIMP_LAYER_MODE_DIFFERENCE:     return blendfun_difference;
+    case GIMP_LAYER_MODE_DARKEN_ONLY:    return blendfun_darken_only;
+    case GIMP_LAYER_MODE_LIGHTEN_ONLY:   return blendfun_lighten_only;
+    case GIMP_LAYER_MODE_VIVID_LIGHT:  return blendfun_vivid_light;
+    case GIMP_LAYER_MODE_PIN_LIGHT:    return blendfun_pin_light;
+    case GIMP_LAYER_MODE_LINEAR_LIGHT: return blendfun_linear_light;
+    case GIMP_LAYER_MODE_EXCLUSION:    return blendfun_exclusion;
+    case GIMP_LAYER_MODE_LINEAR_BURN:  return blendfun_linear_burn;
+    default:
+      return (void*)dummy_fun;
+  }
+}
diff --git a/app/operations/layer-modes/gimpoperationlayermode.h 
b/app/operations/layer-modes/gimpoperationlayermode.h
index c95790b..545203f 100644
--- a/app/operations/layer-modes/gimpoperationlayermode.h
+++ b/app/operations/layer-modes/gimpoperationlayermode.h
@@ -56,5 +56,14 @@ struct _GimpOperationLayerMode
 
 GType   gimp_operation_layer_mode_get_type (void) G_GNUC_CONST;
 
+gboolean
+gimp_operation_layer_mode_process_pixels (GeglOperation       *operation,
+                                          void                *in,
+                                          void                *layer,
+                                          void                *mask,
+                                          void                *out,
+                                          glong                samples,
+                                          const GeglRectangle *roi,
+                                          gint                 level);
 
 #endif /* __GIMP_OPERATION_LAYER_MODE_H__ */
diff --git a/app/widgets/gimpwidgets-constructors.c b/app/widgets/gimpwidgets-constructors.c
index 4c8a875..80e4fa3 100644
--- a/app/widgets/gimpwidgets-constructors.c
+++ b/app/widgets/gimpwidgets-constructors.c
@@ -123,15 +123,20 @@ gimp_paint_mode_menu_new (gboolean with_behind_mode,
                                            GIMP_LAYER_MODE_MULTIPLY_LEGACY,
                                            GIMP_LAYER_MODE_BURN,
                                            GIMP_LAYER_MODE_BURN_LEGACY,
+                                           GIMP_LAYER_MODE_LINEAR_BURN,
                                            GIMP_LAYER_MODE_OVERLAY,
                                            GIMP_LAYER_MODE_SOFTLIGHT,
                                            GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
                                            GIMP_LAYER_MODE_HARDLIGHT,
                                            GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
+                                           GIMP_LAYER_MODE_VIVID_LIGHT,
+                                           GIMP_LAYER_MODE_PIN_LIGHT,
+                                           GIMP_LAYER_MODE_LINEAR_LIGHT,
                                            GIMP_LAYER_MODE_DIFFERENCE,
                                            GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
                                            GIMP_LAYER_MODE_SUBTRACT,
                                            GIMP_LAYER_MODE_SUBTRACT_LEGACY,
+                                           GIMP_LAYER_MODE_EXCLUSION,
                                            GIMP_LAYER_MODE_GRAIN_EXTRACT,
                                            GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
                                            GIMP_LAYER_MODE_GRAIN_MERGE,
@@ -158,10 +163,10 @@ gimp_paint_mode_menu_new (gboolean with_behind_mode,
                                          GIMP_LAYER_MODE_ADDITION_LEGACY, -1);
 
   gimp_int_store_insert_separator_after (GIMP_INT_STORE (store),
-                                         GIMP_LAYER_MODE_BURN_LEGACY, -1);
+                                         GIMP_LAYER_MODE_LINEAR_BURN, -1);
 
   gimp_int_store_insert_separator_after (GIMP_INT_STORE (store),
-                                         GIMP_LAYER_MODE_HARDLIGHT_LEGACY, -1);
+                                         GIMP_LAYER_MODE_LINEAR_LIGHT, -1);
 
   gimp_int_store_insert_separator_after (GIMP_INT_STORE (store),
                                          GIMP_LAYER_MODE_DIVIDE_LEGACY, -1);
diff --git a/libgimp/gimpenums.h b/libgimp/gimpenums.h
index 47fad3a..d322eb5 100644
--- a/libgimp/gimpenums.h
+++ b/libgimp/gimpenums.h
@@ -131,7 +131,17 @@ typedef enum
   GIMP_LAYER_MODE_GRAIN_EXTRACT,
   GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR,
   GIMP_LAYER_MODE_GRAIN_MERGE,
-  GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR
+  GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR,
+  GIMP_LAYER_MODE_VIVID_LIGHT,
+  GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR,
+  GIMP_LAYER_MODE_PIN_LIGHT,
+  GIMP_LAYER_MODE_PIN_LIGHT_LINEAR,
+  GIMP_LAYER_MODE_LINEAR_LIGHT,
+  GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR,
+  GIMP_LAYER_MODE_EXCLUSION,
+  GIMP_LAYER_MODE_EXCLUSION_LINEAR,
+  GIMP_LAYER_MODE_LINEAR_BURN,
+  GIMP_LAYER_MODE_LINEAR_BURN_LINEAR
 } GimpLayerMode;
 
 
diff --git a/tools/pdbgen/enums.pl b/tools/pdbgen/enums.pl
index 5e3da4d..e7dd350 100644
--- a/tools/pdbgen/enums.pl
+++ b/tools/pdbgen/enums.pl
@@ -752,7 +752,17 @@ package Gimp::CodeGen::enums;
                          GIMP_LAYER_MODE_GRAIN_EXTRACT
                          GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR
                          GIMP_LAYER_MODE_GRAIN_MERGE
-                         GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR) ],
+                         GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR
+                         GIMP_LAYER_MODE_VIVID_LIGHT
+                         GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR
+                         GIMP_LAYER_MODE_PIN_LIGHT
+                         GIMP_LAYER_MODE_PIN_LIGHT_LINEAR
+                         GIMP_LAYER_MODE_LINEAR_LIGHT
+                         GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR
+                         GIMP_LAYER_MODE_EXCLUSION
+                         GIMP_LAYER_MODE_EXCLUSION_LINEAR
+                         GIMP_LAYER_MODE_LINEAR_BURN
+                         GIMP_LAYER_MODE_LINEAR_BURN_LINEAR) ],
          mapping => { GIMP_LAYER_MODE_NORMAL_NON_LINEAR => '0',
                       GIMP_LAYER_MODE_DISSOLVE => '1',
                       GIMP_LAYER_MODE_BEHIND => '2',
@@ -815,7 +825,17 @@ package Gimp::CodeGen::enums;
                       GIMP_LAYER_MODE_GRAIN_EXTRACT => '59',
                       GIMP_LAYER_MODE_GRAIN_EXTRACT_LINEAR => '60',
                       GIMP_LAYER_MODE_GRAIN_MERGE => '61',
-                      GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR => '62' }
+                      GIMP_LAYER_MODE_GRAIN_MERGE_LINEAR => '62',
+                      GIMP_LAYER_MODE_VIVID_LIGHT => '63',
+                      GIMP_LAYER_MODE_VIVID_LIGHT_LINEAR => '64',
+                      GIMP_LAYER_MODE_PIN_LIGHT => '65',
+                      GIMP_LAYER_MODE_PIN_LIGHT_LINEAR => '66',
+                      GIMP_LAYER_MODE_LINEAR_LIGHT => '67',
+                      GIMP_LAYER_MODE_LINEAR_LIGHT_LINEAR => '68',
+                      GIMP_LAYER_MODE_EXCLUSION => '69',
+                      GIMP_LAYER_MODE_EXCLUSION_LINEAR => '70',
+                      GIMP_LAYER_MODE_LINEAR_BURN => '71',
+                      GIMP_LAYER_MODE_LINEAR_BURN_LINEAR => '72' }
        },
     GimpBrushApplicationMode =>
        { contig => 1,


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