Add support for extended blend mode. First pass.
authorBenjamin Otte <otte@gnome.org>
Thu, 25 Sep 2008 10:53:06 +0000 (12:53 +0200)
committerSøren Sandmann Pedersen <sandmann@redhat.com>
Tue, 23 Jun 2009 18:42:33 +0000 (14:42 -0400)
This adds support only for FbCombineU function.
This work is based on equations provided in SVG 1.2 specification draft.

http://www.w3.org/TR/SVG12/

Based on a previous patch by Emmanuel Pacaud <emmanuel.pacaud@free.fr>

pixman/pixman-combine.c.template
pixman/pixman-combine.h.template
pixman/pixman.h

index 7d05ce8..2f7f5d1 100644 (file)
@@ -2,12 +2,17 @@
 #include <config.h>
 #endif
 
+#include <math.h>
 #include <string.h>
 
 #include "pixman-private.h"
 
 #include "pixman-combine.h"
 
+#define Red(x) (((x) >> R_SHIFT) & MASK)
+#define Green(x) (((x) >> G_SHIFT) & MASK)
+#define Blue(x) ((x) & MASK)
+
 /*
  * There are two ways of handling alpha -- either as a single unified value or
  * a separate value for each component, hence each macro must have two
@@ -252,6 +257,377 @@ fbCombineSaturateU (pixman_implementation_t *imp, pixman_op_t op,
 }
 
 
+/* Multiply
+ *
+ * Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ * Da'  = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
+ *      = Sa + Da - Sa.Da 
+ */
+
+static void
+fbCombineMultiplyU (pixman_implementation_t *imp, pixman_op_t op,
+                   comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    int i;
+    for (i = 0; i < width; ++i) {
+        comp4_t s = combineMask (src, mask, i);
+        comp4_t d = *(dest + i);
+       comp4_t ss = s;
+        comp4_t src_ia = Alpha (~s);
+       comp4_t dest_ia = Alpha (~d);
+
+       FbByteAddMul (ss, dest_ia, d, src_ia);
+       FbByteMulC (d, s);
+       FbByteAdd (d, ss);      
+       *(dest + i) = d;
+    }
+}
+
+/* Screen
+ *
+ * Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *      = Sca + Dca - Sca.Dca
+ * Da'  = Sa + Da - Sa.Da 
+ */
+
+static void
+fbCombineScreenU (pixman_implementation_t *imp, pixman_op_t op,
+                 comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    int i;
+    for (i = 0; i < width; ++i) {
+        comp4_t s = combineMask (src, mask, i);
+        comp4_t d = *(dest + i);
+       comp4_t p = s;
+       comp4_t a, r, g, b;
+
+       FbByteMulC (p, d);
+
+       a = Alpha (s)   + Alpha (d) - Alpha (p);
+       r = Red(s)      + Red (d)   - Red (p);
+       g = Green (s)   + Green (d) - Green (p);
+       b = Blue (s)    + Blue (d)  - Blue (p);
+
+       /* no clamping required, values don't overflow */
+       *(dest + i) = (a << A_SHIFT) | (r << R_SHIFT) | (g << G_SHIFT) | b;
+    }
+}
+
+#define FbBlendLoop                                \
+    int i;                                         \
+    for (i = 0; i < width; i++)        {                   \
+        comp4_t s = combineMask (src, mask, i);     \
+       comp4_t d = *(dest + i);                    \
+       comp4_t a, r, g, b;                         \
+       comp4_t sada, sca, dca, sa, da, isa, ida;   \
+                                                   \
+       da = Alpha (d);                             \
+       sa = Alpha (s);                             \
+       ida = Alpha (~d);                           \
+       isa = Alpha (~s);                           \
+       sada = sa * da;                             \
+                                                   \
+       a = sa + da - DivOne (sada);                \
+                                                   \
+       sca = Red (s);                              \
+       dca = Red (d);                              \
+       FbBlendOp (r);                              \
+                                                   \
+       sca = Green (s);                            \
+       dca = Green (d);                            \
+       FbBlendOp (g);                              \
+                                                   \
+       sca = Blue (s);                             \
+       dca = Blue (d);                             \
+       FbBlendOp (b);                              \
+                                                   \
+       *(dest + i) = (a << 24)                     \
+           | ((r & MASK) << R_SHIFT)               \
+           | ((g & MASK) << G_SHIFT)               \
+           |  (b & MASK);                          \
+    } while (0);
+
+/* Overlay
+ *
+ * if 2.Dca < Da
+ *     Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ *   otherwise
+ *     Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ * Da' = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       if (2 * dca  < da)                                              \
+           rca = 2 * sca * dca;                                        \
+       else                                                            \
+           rca = sada - 2 * (da - dca) * (sa - sca);                   \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineOverlayU (pixman_implementation_t *imp, pixman_op_t op,
+                  comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Darken
+ *
+ * Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ * Da'  = Sa + Da - Sa.Da 
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       rca = sca * da;                                                 \
+       if (rca > dca * sa)                                             \
+           rca = dca * sa;                                             \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineDarkenU (pixman_implementation_t *imp, pixman_op_t op,
+                 comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Lighten
+ *
+ * Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       rca = sca * da;                                                 \
+       if (rca < dca * sa)                                             \
+           rca = dca * sa;                                             \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineLightenU (pixman_implementation_t *imp, pixman_op_t op,
+                  comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Color dodge
+ *
+ * if Sca.Da + Dca.Sa >= Sa.Da
+ *   Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+ * otherwise
+ *   Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */ 
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       if ((sca * da + dca * sa >= sada) || (sa == sca))               \
+           rca = sada;                                                 \
+       else                                                            \
+           rca = sa * sa * dca / (sa - sca);                           \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineColorDodgeU (pixman_implementation_t *imp, pixman_op_t op,
+                     comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Color burn
+ *
+ * if Sca.Da + Dca.Sa <= Sa.Da
+ *   Dca' = Sca.(1 - Da) + Dca.(1 - Sa)
+ * otherwise
+ *   Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       if ((sca * da + dca * sa <= sada) || (sca == 0))                \
+           rca = sca * ida + dca * isa;                                \
+       else                                                            \
+           rca = sa * (sca * da + sa * dca - sada) / sca               \
+                  + sca * ida                                          \
+                  + dca * isa ;                                        \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineColorBurnU (pixman_implementation_t *imp, pixman_op_t op,
+                    comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Hard light
+ *
+ * if 2.Sca < Sa
+ *   Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ * otherwise
+ *   Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       if (2 * sca < sa)                                               \
+           rca = 2 * sca * dca;                                        \
+       else                                                            \
+           rca = sada - 2 * (da - dca) * (sa - sca);                   \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineHardLightU (pixman_implementation_t *imp, pixman_op_t op,
+                    comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Soft light
+ *
+ * original definition (taken from PDF spec):
+ * 
+ * if (Sc <= 0.5)
+ *   Dc' = Dc - ( 1 - 2 . Sc ) . Dc . ( 1 - Dc )
+ * else
+ *   Dc' = Dc + ( 2 . Sc - 1 ) . ( F(Dc) – Dc )
+ *
+ * with
+ * if (x < 0.25)
+ *   F(x) = ( ( 16 . x - 12 ) . x + 4 ) . x
+ * else
+ *   F(x) = SQRT (x)
+ *
+ * ==> (taken from SVG spec)
+ *
+ * if (2.Sca <= Sa)
+ *   Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa) 
+ * otherwise if Dca.8 <= Da
+ *   Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa).(3 - 8.Dca/Da)) + Sc.(1 - Da) + Dca.(1 - Sa)
+ * otherwise
+ *   Dca' = (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *   
+ * ==> (used here)
+ * if (2.Sca <= Sa)
+ *   Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa))
+ * otherwise if Dca.8 <= Da
+ *   Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa).(3 - 8.Dca/Da))
+ * otherwise
+ *   Dca' = (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa))
+ * Dca' += Sca.(1 - Da) + Dca.(1 - Sa);
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                                 \
+    do {                                                                               \
+       if (2 * sca < sa) {                                                             \
+           if (dca == da || da == 0)                                                   \
+               rca = dca * sa;                                                         \
+           else                                                                        \
+               rca = dca * (sa - (da - dca) * (2 * sca + sa) / da);                    \
+       } else if (da == 0) {                                                           \
+           rca = 0;                                                                    \
+       } else if (8 * dca <= da) {                                                     \
+           int tmp = dca * (sa - (da - dca) * (2 * sca - sa) / da * (3 * da - 8 * dca) / da);  \
+           rca = tmp < 0 ? -tmp : tmp;                                                 \
+        } else {                                                                       \
+           rca = dca * sa + ((comp4_t) (sqrt (dca * da) - dca)) * (2 * sca - sa);      \
+       }                                                                               \
+       rca += sca * ida + dca * isa;                                                   \
+       rca = DivOne (rca);                                                             \
+    } while (0);
+
+static void
+fbCombineSoftLightU (pixman_implementation_t *imp, pixman_op_t op,
+                    comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Difference
+ *
+ * Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *     = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       comp4_t dcasa = dca * sa;                                       \
+       rca = sca * da;                                                 \
+       if (rca < dcasa)                                                \
+           rca = dcasa - rca;                                          \
+       else                                                            \
+           rca -= dcasa;                                               \
+       rca += sca * ida + dca * isa;                                   \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineDifferenceU (pixman_implementation_t *imp, pixman_op_t op,
+                     comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+/* Exclusion
+ *
+ * Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ *
+ * Da'  = Sa + Da - Sa.Da
+ */
+
+#define FbBlendOp(rca)                                                 \
+    do {                                                               \
+       rca = dca * MASK + sca * (MASK - 2 * dca);                      \
+       rca = DivOne (rca);                                             \
+    } while (0);
+
+static void
+fbCombineExclusionU (pixman_implementation_t *imp, pixman_op_t op,
+                    comp4_t *dest, const comp4_t *src, const comp4_t *mask, int width)
+{
+    FbBlendLoop
+}
+
+#undef FbBlendOp
+
+#undef FbBlendLoop
+
 /*
  * All of the disjoint composing functions
 
@@ -1297,6 +1673,18 @@ _pixman_setup_combiner_functions_width (pixman_implementation_t *imp)
     imp->combine_width[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = fbCombineConjointAtopReverseU;
     imp->combine_width[PIXMAN_OP_CONJOINT_XOR] = fbCombineConjointXorU;
 
+    imp->combine_width[PIXMAN_OP_MULTIPLY] = fbCombineMultiplyU;
+    imp->combine_width[PIXMAN_OP_SCREEN] = fbCombineScreenU;
+    imp->combine_width[PIXMAN_OP_OVERLAY] = fbCombineOverlayU;
+    imp->combine_width[PIXMAN_OP_DARKEN] = fbCombineDarkenU;
+    imp->combine_width[PIXMAN_OP_LIGHTEN] = fbCombineLightenU;
+    imp->combine_width[PIXMAN_OP_COLOR_DODGE] = fbCombineColorDodgeU;
+    imp->combine_width[PIXMAN_OP_COLOR_BURN] = fbCombineColorBurnU;
+    imp->combine_width[PIXMAN_OP_HARD_LIGHT] = fbCombineHardLightU;
+    imp->combine_width[PIXMAN_OP_SOFT_LIGHT] = fbCombineSoftLightU;
+    imp->combine_width[PIXMAN_OP_DIFFERENCE] = fbCombineDifferenceU;
+    imp->combine_width[PIXMAN_OP_EXCLUSION] = fbCombineExclusionU;
+
     /* Component alpha combiners */
     imp->combine_width_ca[PIXMAN_OP_CLEAR] = fbCombineClearC;
     imp->combine_width_ca[PIXMAN_OP_SRC] = fbCombineSrcC;
@@ -1341,3 +1729,4 @@ _pixman_setup_combiner_functions_width (pixman_implementation_t *imp)
     imp->combine_width_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = fbCombineConjointAtopReverseC;
     imp->combine_width_ca[PIXMAN_OP_CONJOINT_XOR] = fbCombineConjointXorC;
 }
+
index f2e58a1..6828fea 100644 (file)
@@ -29,6 +29,8 @@
 #define Add(x,y,i,t)   ((t) = GetComp(x,i) + GetComp(y,i),              \
                         (comp4_t) ((comp1_t) ((t) | (0 - ((t) >> G_SHIFT)))) << (i))
 
+#define DivOne(x)      (((x) + ONE_HALF + (((x) + ONE_HALF) >> G_SHIFT)) >> G_SHIFT)
+
 /*
   The methods below use some tricks to be able to do two color
   components at the same time.
index a954d2c..a56f55a 100644 (file)
@@ -368,6 +368,18 @@ typedef enum
     PIXMAN_OP_CONJOINT_ATOP_REVERSE    = 0x2a,
     PIXMAN_OP_CONJOINT_XOR             = 0x2b,
 
+    PIXMAN_OP_MULTIPLY                  = 0x30,
+    PIXMAN_OP_SCREEN                    = 0x31,
+    PIXMAN_OP_OVERLAY                   = 0x32,
+    PIXMAN_OP_DARKEN                    = 0x33,
+    PIXMAN_OP_LIGHTEN                   = 0x34,
+    PIXMAN_OP_COLOR_DODGE               = 0x35,
+    PIXMAN_OP_COLOR_BURN                = 0x36,
+    PIXMAN_OP_HARD_LIGHT                = 0x37,
+    PIXMAN_OP_SOFT_LIGHT                = 0x38,
+    PIXMAN_OP_DIFFERENCE                = 0x39,
+    PIXMAN_OP_EXCLUSION                 = 0x3a,
+
     PIXMAN_OP_NONE,
     PIXMAN_OP_LAST = PIXMAN_OP_NONE
 } pixman_op_t;