}
}
-
-/* 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
} \
}
-/* 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
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
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
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
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
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)
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
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
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
#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)
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)
{
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)
{
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)
{
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)
{