gimp r27409 - in trunk: . app/gegl



Author: martinn
Date: Sat Oct 25 22:44:19 2008
New Revision: 27409
URL: http://svn.gnome.org/viewvc/gimp?rev=27409&view=rev

Log:
* app/gegl/gimpoperationpointlayermode.c: Implement a bunch of
layer modes. Use the exact SVG 1.2 formula for layer modes that
have a counterpart in SVG 1.2. Don't clamp the result to [0..1]
for Dodge and Burn though as we don't need to. Maybe we *should*
clamp from a compositing point of view, I'm not sure. Also
reformat the code a bit for readability.

Keep in mind that we now treat the opacity of all layers the same
indepentant of the layer mode. That is why most of the new
implementations doesn't work the same as the legacy ones when
transparency is involved, only when the layers are completely
opaque. Another important property for all layer modes implemented
below is that compositing onto complete transparency gives the
same result as if the layer would have been in Normal blending
mode.

The status of the new layer mode implementations compared to the
legacy implementations is as follows:

Completely works the same:
 o Behind

Works the same for 100% opaque layers:
 o Multiply
 o Screen
 o Difference
 o Darken
 o Lighten
 o Dodge
 o Burn
 o Hard Light
 o Subtract
 o Divide

Works different but similar:
 o Overlay

Work in progress:
 o Soft Light


Modified:
   trunk/ChangeLog
   trunk/app/gegl/gimpoperationpointlayermode.c

Modified: trunk/app/gegl/gimpoperationpointlayermode.c
==============================================================================
--- trunk/app/gegl/gimpoperationpointlayermode.c	(original)
+++ trunk/app/gegl/gimpoperationpointlayermode.c	Sat Oct 25 22:44:19 2008
@@ -29,6 +29,12 @@
 #include "gimpoperationpointlayermode.h"
 
 
+#define R RED
+#define G GREEN
+#define B BLUE
+#define A ALPHA
+
+
 enum
 {
   PROP_0,
@@ -161,34 +167,155 @@
 {
   GimpOperationPointLayerMode *self = GIMP_OPERATION_POINT_LAYER_MODE (operation);
 
-  gfloat *in    = in_buf;
-  gfloat *layer = aux_buf;
-  gfloat *out   = out_buf;
+  gfloat *in  = in_buf;  /* composite of layers below */
+  gfloat *lay = aux_buf; /* layer */
+  gfloat *out = out_buf; /* resulting composite */
 
   while (samples--)
     {
-      out[ALPHA] = in[ALPHA] + layer[ALPHA] - in[ALPHA] * layer[ALPHA];
+      /* Alpha is treated the same */
+      out[A] = lay[A] + in[A] - lay[A] * in[A];
 
       switch (self->blend_mode)
         {
         case GIMP_NORMAL_MODE:
-          out[RED]   = layer[RED]   + in[RED]   * (1 - layer[ALPHA]);
-          out[GREEN] = layer[GREEN] + in[GREEN] * (1 - layer[ALPHA]);
-          out[BLUE]  = layer[BLUE]  + in[BLUE]  * (1 - layer[ALPHA]);
+          /* Porter-Duff A over B */
+          out[R] = lay[R] + in[R] * (1 - lay[A]);
+          out[G] = lay[G] + in[G] * (1 - lay[A]);
+          out[B] = lay[B] + in[B] * (1 - lay[A]);
           break;
 
-        case GIMP_DISSOLVE_MODE:
-          g_warning ("Not a point filter and cannot be implemented here.");
-          break;
-          
         case GIMP_BEHIND_MODE:
+          /* Porter-Duff B over A */
+          out[R] = in[R] + lay[R] * (1 - in[A]);
+          out[G] = in[G] + lay[G] * (1 - in[A]);
+          out[B] = in[B] + lay[B] * (1 - in[A]);
+          break;
+
+
         case GIMP_MULTIPLY_MODE:
+          /* SVG 1.2 multiply */
+          out[R] = lay[R] * in[R] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+          out[G] = lay[G] * in[G] + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+          out[B] = lay[B] * in[B] + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+          break;
+
         case GIMP_SCREEN_MODE:
+          /* SVG 1.2 screen */
+          out[R] = lay[R] + in[R] - lay[R] * in[R];
+          out[G] = lay[G] + in[G] - lay[G] * in[G];
+          out[B] = lay[B] + in[B] - lay[B] * in[B];
+          break;
+
         case GIMP_OVERLAY_MODE:
+          /* SVG 1.2 overlay */
+          if (2 * in[R] < in[A])
+            {
+              out[R] = 2 * lay[R] * in[R] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = 2 * lay[G] * in[G] + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = 2 * lay[B] * in[B] + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          else
+            {
+              out[R] = lay[A] * in[A] - 2 * (in[A] - in[R]) * (lay[A] - lay[R]) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = lay[A] * in[A] - 2 * (in[A] - in[G]) * (lay[A] - lay[G]) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = lay[A] * in[A] - 2 * (in[A] - in[B]) * (lay[A] - lay[B]) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          break;
+
         case GIMP_DIFFERENCE_MODE:
-          /* TODO */
+          /* SVG 1.2 difference */
+          out[R] = in[R] + lay[R] - 2 * MIN (lay[R] * in[A], in[R] * lay[A]);
+          out[G] = in[G] + lay[G] - 2 * MIN (lay[G] * in[A], in[G] * lay[A]);
+          out[B] = in[B] + lay[B] - 2 * MIN (lay[B] * in[A], in[B] * lay[A]);
+          break;
+
+        case GIMP_DARKEN_ONLY_MODE:
+          /* SVG 1.2 darken */
+          out[R] = MIN (lay[R] * in[A], in[R] * lay[A]) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+          out[G] = MIN (lay[G] * in[A], in[G] * lay[A]) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+          out[B] = MIN (lay[B] * in[A], in[B] * lay[A]) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+          break;
+
+        case GIMP_LIGHTEN_ONLY_MODE:
+          /* SVG 1.2 lighten */
+          out[R] = MAX (lay[R] * in[A], in[R] * lay[A]) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+          out[G] = MAX (lay[G] * in[A], in[G] * lay[A]) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+          out[B] = MAX (lay[B] * in[A], in[B] * lay[A]) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+          break;
+
+        case GIMP_DODGE_MODE:
+          /* SVG 1.2 color-dodge with disabled [0..1] clamping */
+          /* if (lay[R] * in[A] + in[R] * lay[A] >= lay[A] * in[A])
+            {
+              out[R] = lay[A] * in[A] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[R] = lay[A] * in[A] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[R] = lay[A] * in[A] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+            }
+          else */
+            {
+              out[R] = in[R] * lay[A] / (1 - lay[R] / lay[A]) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = in[G] * lay[A] / (1 - lay[G] / lay[A]) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = in[B] * lay[A] / (1 - lay[B] / lay[A]) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
           break;
 
+        case GIMP_BURN_MODE:
+          /* SVG 1.2 color-burn with disabled [0..1] clamping */
+          /* if (lay[R] * in[A] + in[R] * lay[A] <= lay[A] * in[A])
+            {
+              out[R] = lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          else */
+            {
+              out[R] = lay[A] * (lay[R] * in[A] + in[R] * lay[A] - lay[A] * in[A])/lay[R] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = lay[A] * (lay[G] * in[A] + in[G] * lay[A] - lay[A] * in[A])/lay[G] + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = lay[A] * (lay[B] * in[A] + in[B] * lay[A] - lay[A] * in[A])/lay[B] + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          break;
+
+        case GIMP_HARDLIGHT_MODE:
+          /* SVG 1.2 hard-light */
+          if (2 * lay[R] < lay[A])
+            {
+              out[R] = 2 * lay[R] * in[R] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = 2 * lay[G] * in[G] + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = 2 * lay[B] * in[B] + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          else
+            {
+              out[R] = lay[A] * in[A] - 2 * (in[A] - in[R]) * (lay[A] - lay[R]) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = lay[A] * in[A] - 2 * (in[A] - in[G]) * (lay[A] - lay[G]) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = lay[A] * in[A] - 2 * (in[A] - in[B]) * (lay[A] - lay[B]) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          break;
+
+        case GIMP_SOFTLIGHT_MODE:
+          /* SVG 1.2 soft-light */
+          /* XXX: Why is the result so different from legacy Soft Light? */
+          if (2 * lay[R] < lay[A])
+            {
+              out[R] = in[R] * (lay[A] - (1 - in[R]/in[A]) * (2 * lay[R] - lay[A])) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = in[G] * (lay[A] - (1 - in[G]/in[A]) * (2 * lay[G] - lay[A])) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = in[B] * (lay[A] - (1 - in[B]/in[A]) * (2 * lay[B] - lay[A])) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          else if (8 * in[R] <= in[A])
+            {
+              out[R] = in[R] * (lay[A] - (1 - in[R]/in[A]) * (2 * lay[R] - lay[A]) * (3 - 8 * in[R]/in[A])) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = in[G] * (lay[A] - (1 - in[G]/in[A]) * (2 * lay[G] - lay[A]) * (3 - 8 * in[G]/in[A])) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = in[B] * (lay[A] - (1 - in[B]/in[A]) * (2 * lay[B] - lay[A]) * (3 - 8 * in[B]/in[A])) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          else
+            {
+              out[R] = (in[R] * lay[A] + (sqrt (in[R] / in[A]) * in[A] - in[R]) * (2 * lay[R] - lay[A])) + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+              out[G] = (in[G] * lay[A] + (sqrt (in[G] / in[A]) * in[A] - in[G]) * (2 * lay[G] - lay[A])) + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+              out[B] = (in[B] * lay[A] + (sqrt (in[B] / in[A]) * in[A] - in[B]) * (2 * lay[B] - lay[A])) + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+            }
+          break;
+
+
         case GIMP_ADDITION_MODE:
           /* To be more mathematically correct we would have to either
            * adjust the formula for the resulting opacity or adapt the
@@ -200,23 +327,29 @@
            * interpreted is more important than mathematically correct
            * results, we don't bother.
            */
-          out[RED]   = in[RED]   + layer[RED];
-          out[GREEN] = in[GREEN] + layer[GREEN];
-          out[BLUE]  = in[BLUE]  + layer[BLUE];
+          out[R] = in[R] + lay[R];
+          out[G] = in[G] + lay[G];
+          out[B] = in[B] + lay[B];
           break;
 
         case GIMP_SUBTRACT_MODE:
-        case GIMP_DARKEN_ONLY_MODE:
-        case GIMP_LIGHTEN_ONLY_MODE:
+          /* Derieved from SVG 1.2 formulas */
+          out[R] = in[R] + lay[R] - 2 * lay[R] * in[A];
+          out[G] = in[G] + lay[G] - 2 * lay[G] * in[A];
+          out[B] = in[B] + lay[B] - 2 * lay[B] * in[A];
+          break;
+
+        case GIMP_DIVIDE_MODE:
+          /* Derieved from SVG 1.2 formulas */
+          out[R] = in[R] / lay[R] + lay[R] * (1 - in[A]) + in[R] * (1 - lay[A]);
+          out[G] = in[G] / lay[G] + lay[G] * (1 - in[A]) + in[G] * (1 - lay[A]);
+          out[B] = in[B] / lay[B] + lay[B] * (1 - in[A]) + in[B] * (1 - lay[A]);
+          break;
+
         case GIMP_HUE_MODE:
         case GIMP_SATURATION_MODE:
         case GIMP_COLOR_MODE:
         case GIMP_VALUE_MODE:
-        case GIMP_DIVIDE_MODE:
-        case GIMP_DODGE_MODE:
-        case GIMP_BURN_MODE:
-        case GIMP_HARDLIGHT_MODE:
-        case GIMP_SOFTLIGHT_MODE:
         case GIMP_GRAIN_EXTRACT_MODE:
         case GIMP_GRAIN_MERGE_MODE:
         case GIMP_COLOR_ERASE_MODE:
@@ -226,14 +359,20 @@
           /* TODO */
           break;
 
+
+        case GIMP_DISSOLVE_MODE:
+          /* Not a point filter and cannot be implemented here */
+          /* g_assert_not_reached (); */
+          break;
+
         default:
           g_error ("Unknown layer mode");
           break;
         }
 
-      in    += 4;
-      layer += 4;
-      out   += 4;
+      in  += 4;
+      lay += 4;
+      out += 4;
     }
 
   return TRUE;



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