rework blend-mode documentation to match current code better
authorBenjamin Otte <otte@gnome.org>
Sun, 23 Nov 2008 14:42:53 +0000 (15:42 +0100)
committerSøren Sandmann Pedersen <sandmann@redhat.com>
Tue, 23 Jun 2009 18:42:35 +0000 (14:42 -0400)
pixman/pixman-combine.c.template

index 41e174c..78eac3d 100644 (file)
@@ -336,12 +336,32 @@ fbCombineSaturateU (pixman_implementation_t *imp, pixman_op_t op,
     }
 }
 
-
-/* Multiply
+/* 
+ * PDF blend modes:
+ * The following blend modes have been taken from the PDF ISO 32000
+ * specification, which at this point in time is available from
+ * http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf
+ * The relevant chapters are 11.3.5 and 11.3.6.
+ * The formula for computing the final pixel color given in 11.3.6 is:
+ * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs)
+ * with B() being the blend function.
+ * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
+ *
+ * These blend modes should match the SVG filter draft specification, as 
+ * it has been designed to mirror ISO 32000. Note that at the current point
+ * no released draft exists that shows this, as the formulas have not been
+ * updated yet after the release of ISO 32000.
  *
- * Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
- * Da'  = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
- *      = Sa + Da - Sa.Da 
+ * The default implementation here uses the PdfSeparableBlendMode and 
+ * PdfNonSeparableBlendMode macros, which take the blend function as an 
+ * argument. Note that this implementation operates on premultiplied colors,
+ * while the PDF specification does not. Therefore the code uses the formula
+ * ar.Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
+ */
+
+/* 
+ * Multiply
+ * B(Dca, ad, Sca, as) = Dca.Sca
  */
 
 static void
@@ -463,11 +483,9 @@ fbCombine ## name ## C (pixman_implementation_t *imp, pixman_op_t op, \
     }                                              \
 }
 
-/* Screen
- *
- * Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
- *      = Sca + Dca - Sca.Dca
- * Da'  = Sa + Da - Sa.Da 
+/*
+ * Screen
+ * B(Dca, ad, Sca, as) = Dca.sa + Sca.da - Dca.Sca
  */
 
 static inline comp4_t
@@ -478,13 +496,13 @@ BlendScreen (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (Screen)
 
-/* Overlay
- *
- * if 2.Dca < Da
- *     Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+/*
+ * Overlay
+ * B(Dca, ab, Sca, as) = 
+ *   if 2.Dca < Da
+ *     2.Sca.Dca
  *   otherwise
- *     Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
- * Da' = Sa + Da - Sa.Da
+ *     Sa.Da - 2.(Da - Dca).(Sa - Sca)
  */
 
 static inline comp4_t
@@ -501,10 +519,9 @@ BlendOverlay (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (Overlay)
 
-/* Darken
- *
- * Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
- * Da'  = Sa + Da - Sa.Da 
+/*
+ * Darken
+ * B(Dca, ab, Sca, as) = min (Sca.Da, Dca.Sa)
  */
 
 static inline comp4_t
@@ -519,10 +536,9 @@ BlendDarken (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (Darken)
 
-/* Lighten
- *
- * Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
- * Da'  = Sa + Da - Sa.Da
+/*
+ * Lighten
+ * B(Dca, ab, Sca, as) = max (Sca.Da, Dca.Sa)
  */
 
 static inline comp4_t
@@ -537,14 +553,13 @@ BlendLighten (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (Lighten)
 
-/* Color dodge
- *
- * if Sca == Sa
- *   Dca' = (Dca != 0).Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
- * otherwise
- *   Dca' = Da.Sa. min (Dca / Da / (1 - Sca/Sa))
- *
- * Da'  = Sa + Da - Sa.Da
+/*
+ * Color dodge
+ * B(Dca, ab, Sca, as) = 
+ *   if Sca == Sa
+ *     (Dca != 0).Sa.Da
+ *   otherwise
+ *     Da.Sa. min (Dca / Da / (1 - Sca/Sa))
  */ 
 
 static inline comp4_t
@@ -560,38 +575,36 @@ BlendColorDodge (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (ColorDodge)
 
-/* Color burn
- *
- * if Sca. == 0
- *   Dca' = (Da == Dca).SaDa + Sca.(1 - Da) + Dca.(1 - Sa)
- * otherwise
- *   Dca' = Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca)) + Sca.(1 - Da) + Dca.(1 - Sa)
- *
- * Da' = Sa + Da - Sa.Da
+/*
+ * Color burn
+ * B(Dca, ab, Sca, as) = 
+ *   if Sca. == 0
+ *     (Da == Dca).SaDa
+ *   otherwise
+ *     Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca))
  */
 
 static inline comp4_t
 BlendColorBurn (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 {
     if (sca == 0) {
-      return (da == dca) ? DivOne (sa * da) : 0;
+       return (da == dca) ? DivOne (sa * da) : 0;
     } else {
-      comp4_t sada = sa * da;
-      comp4_t rca = (da - dca) * sa * sa / sca;
-      return DivOne (rca > sada ? 0 : sada - rca);
+       comp4_t sada = sa * da;
+       comp4_t rca = (da - dca) * sa * sa / sca;
+       return DivOne (rca > sada ? 0 : sada - rca);
     }
 }
 
 PdfSeparableBlendMode (ColorBurn)
 
-/* 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
+/*
+ * Hard light
+ * B(Dca, ab, Sca, as) = 
+ *   if 2.Sca < Sa
+ *     2.Sca.Dca
+ *   otherwise
+ *     Sa.Da - 2.(Da - Dca).(Sa - Sca)
  */
 static inline comp4_t
 BlendHardLight (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
@@ -604,38 +617,15 @@ BlendHardLight (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (HardLight)
 
-/* 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)
- *
- * ==>
- *
- * if (Sc <= 0.5)
- *   Dc' = Dc - ( 1 - 2 . Sc ) . Dc . ( 1 - Dc )
- * otherwise if (Dc < 0.25)
- *   Dc' = Dc + ( 2 . Sc - 1 ) . ( ((16 . Dc - 12) . Dc + 4) - Dc )
- * otherwise
- *   Dc' = Dc + ( 2 . Sc - 1 ) . ( SQRT(Dc) - Dc )
- *
- * ==> 
- *
- * if (2.Sca <= Sa)
- *   Dca' = Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa) 
- * otherwise if Dca.4 <= Da
- *   Dca' = Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3) + Sc.(1 - Da) + Dca.(1 - Sa)
- * otherwise
- *   Dca' = (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa)
+/*
+ * Soft light
+ * B(Dca, ab, Sca, as) = 
+ *   if (2.Sca <= Sa)
+ *     Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa))
+ *   otherwise if Dca.4 <= Da
+ *     Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3)
+ *   otherwise
+ *     (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa))
  */
 
 static inline comp4_t
@@ -664,12 +654,9 @@ BlendSoftLight (comp4_t dca_org, comp4_t da_org, comp4_t sca_org, comp4_t sa_org
 
 PdfSeparableBlendMode (SoftLight)
 
-/* 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
+/*
+ * Difference
+ * B(Dca, ab, Sca, as) = abs (Dca.Sa - Sca.Da)
  */
 
 static inline comp4_t
@@ -686,11 +673,9 @@ BlendDifference (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
 
 PdfSeparableBlendMode (Difference)
 
-/* Exclusion
- *
- * Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
- *
- * Da'  = Sa + Da - Sa.Da
+/*
+ * Exclusion
+ * B(Dca, ab, Sca, as) = (Sca.Da + Dca.Sa - 2.Sca.Dca)
  */
 
 /* This can be made faster by writing it directly and not using
@@ -772,6 +757,37 @@ fbCombineSubtractC (pixman_implementation_t *imp, pixman_op_t op,
 
 #undef Subtract
 
+/*
+ * PDF nonseperable blend modes are implemented using the following functions
+ * to operate in HSL space, with Cmax, Cmid, Cmin referring to the max, mid 
+ * and min value of the red, green and blue components.
+ * 
+ * Lum (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue
+ *
+ * SetLum (C, l):
+ *   d = l – Lum (C)
+ *   C += d
+ *   l = Lum (C)
+ *   min = Cmin
+ *   max = Cmax
+ *   if n < 0.0
+ *     C = l + ( ( ( C – l ) × l ) ⁄ ( l – min ) )
+ *   if x > 1.0
+ *     C = l + ( ( ( C – l ) × ( 1 – l ) ) ⁄ ( max – l ) )
+ *   return C
+ *
+ * Sat (C) = Max (C) - Min (C)
+ *
+ * SetSat (C, s):
+ *  if Cmax > Cmin
+ *    Cmid = ( ( ( Cmid – Cmin ) × s ) ⁄ ( Cmax – Cmin ) )
+ *    Cmax = s
+ *  else
+ *    Cmid = Cmax = 0.0
+ *  Cmin = 0.0
+ *  return C
+ */
+
 #define Min(c) (c[0] < c[1] ? (c[0] < c[2] ? c[0] : c[2]) : (c[1] < c[2] ? c[1] : c[2]))
 #define Max(c) (c[0] > c[1] ? (c[0] > c[2] ? c[0] : c[2]) : (c[1] > c[2] ? c[1] : c[2]))
 #define Lum(c) ((c[0] * 30 + c[1] * 59 + c[2] * 11) / 100)
@@ -947,6 +963,10 @@ SetSat (comp4_t dest[3], comp4_t src[3], comp4_t sat)
   if (Max (dest) > 255 * 255) while (1);                   \
 }
 
+/*
+ * Hue:
+ * B(Cb, Cs) = SetLum (SetSat (Cs, Sat (Cb)), Lum (Cb))
+ */
 static inline void
 BlendHSLHue (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t sa)
 {
@@ -959,6 +979,10 @@ BlendHSLHue (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t sa)
 
 PdfNonSeparableBlendMode (HSLHue)
 
+/*
+ * Saturation:
+ * B(Cb, Cs) = SetLum (SetSat (Cb, Sat (Cs)), Lum (Cb))
+ */
 static inline void
 BlendHSLSaturation (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t sa)
 {
@@ -971,6 +995,10 @@ BlendHSLSaturation (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp
 
 PdfNonSeparableBlendMode (HSLSaturation)
 
+/*
+ * Color:
+ * B(Cb, Cs) = SetLum (Cs, Lum (Cb))
+ */
 static inline void
 BlendHSLColor (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t sa)
 {
@@ -982,6 +1010,10 @@ BlendHSLColor (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t s
 
 PdfNonSeparableBlendMode (HSLColor)
 
+/*
+ * Luminosity:
+ * B(Cb, Cs) = SetLum (Cb, Lum (Cs))
+ */
 static inline void
 BlendHSLLuminosity (comp4_t c[3], comp4_t dc[3], comp4_t da, comp4_t sc[3], comp4_t sa)
 {