Added support for non-separable blending modes.
Fixed scalar issue from https://codereview.appspot.com/7346044

Review URL: https://chromiumcodereview.appspot.com/12393049

git-svn-id: http://skia.googlecode.com/svn/trunk@7984 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index c0a825c..ddcccec 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -431,6 +431,233 @@
     return SkPackARGB32(a, r, g, b);
 }
 
+// The CSS compositing spec introduces the following formulas:
+// (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable)
+// SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709
+// while PDF and CG uses the one from Rec. Rec. 601
+// See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm
+static inline int Lum(int r, int g, int b)
+{
+    return (r * 77 + g * 151 + b * 28) >> 8;
+}
+
+static inline int min2(int a, int b) { return a < b ? a : b; }
+static inline int max2(int a, int b) { return a > b ? a : b; }
+#define minimum(a, b, c) min2(min2(a, b), c) 
+#define maximum(a, b, c) max2(max2(a, b), c) 
+
+static inline int Sat(int r, int g, int b) {
+    return maximum(r, g, b) - minimum(r, g, b);
+}
+
+static inline void setSaturationComponents(int* Cmin, int* Cmid, int* Cmax, int s) {
+    if(Cmax > Cmin) {
+        *Cmid =  (((*Cmid - *Cmin) * s ) / (*Cmax - *Cmin));
+        *Cmax = s;
+    } else {
+        *Cmax = 0;
+        *Cmid = 0;
+    }
+
+    *Cmin = 0;
+}
+
+static inline void SetSat(int* r, int* g, int* b, int s) {
+    if(*r <= *g) {
+        if(*g <= *b) {
+            setSaturationComponents(r, g, b, s);
+        } else if(*r <= *b) {
+            setSaturationComponents(r, b, g, s);
+        } else {
+            setSaturationComponents(b, r, g, s);
+        }
+    } else if(*r <= *b) {
+        setSaturationComponents(g, r, b, s);
+    } else if(*g <= *b) {
+        setSaturationComponents(g, b, r, s);
+    } else {
+        setSaturationComponents(b, g, r, s);
+    }
+}
+
+static inline void clipColor(int* r, int* g, int* b) {
+    int L = Lum(*r, *g, *b);
+    int n = minimum(*r, *g, *b);
+    int x = maximum(*r, *g, *b);
+    if(n < 0) {
+       *r = L + (((*r - L) * L) / (L - n));
+       *g = L + (((*g - L) * L) / (L - n));
+       *b = L + (((*b - L) * L) / (L - n));
+    }
+
+    if(x > 255) {
+       *r = L + (((*r - L) * (255 - L)) / (x - L));
+       *g = L + (((*g - L) * (255 - L)) / (x - L));
+       *b = L + (((*b - L) * (255 - L)) / (x - L));
+    }
+}
+
+static inline void SetLum(int* r, int* g, int* b, int l) {
+  int d = l - Lum(*r, *g, *b);
+  *r +=  d;
+  *g +=  d;
+  *b +=  d;
+
+  clipColor(r, g, b);
+}
+
+// non-separable blend modes are done in non-premultiplied alpha
+#define  blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
+  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  +  clamp_div255round(sa * da) * blendval)
+
+// kHue_Mode
+// B(Cb, Cs) = SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb))
+// Create a color with the hue of the source color and the saturation and luminosity of the backdrop color.
+static SkPMColor hue_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+    int Sr, Sg, Sb;
+
+    if(sa && da) {
+        Sr = SkMulDiv255Round(sr, sa);
+        Sg = SkMulDiv255Round(sg, sa);
+        Sb = SkMulDiv255Round(sb, sa);
+        int Dr = SkMulDiv255Round(dr, da);
+        int Dg = SkMulDiv255Round(dg, da);
+        int Db = SkMulDiv255Round(db, da);
+        SetSat(&Sr, &Sg, &Sb, Sat(Dr, Dg, Db));
+        SetLum(&Sr, &Sg, &Sb, Lum(Dr, Dg, Db));
+    } else {
+        Sr = 0;
+        Sg = 0;
+        Sb = 0;
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kSaturation_Mode
+// B(Cb, Cs) = SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb))
+// Create a color with the saturation of the source color and the hue and luminosity of the backdrop color. 
+static SkPMColor saturation_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+    int Dr, Dg, Db;
+
+    if(sa && da) {
+        int Sr = SkMulDiv255Round(sr, sa);
+        int Sg = SkMulDiv255Round(sg, sa);
+        int Sb = SkMulDiv255Round(sb, sa);
+        Dr = SkMulDiv255Round(dr, da);
+        Dg = SkMulDiv255Round(dg, da);
+        Db = SkMulDiv255Round(db, da);
+        int LumD = Lum(Dr, Dg, Db);
+        SetSat(&Dr, &Dg, &Db, Sat(Sr, Sg, Sb));
+        SetLum(&Dr, &Dg, &Db, LumD);
+    } else {
+        Dr = 0;
+        Dg = 0;
+        Db = 0;
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kColor_Mode
+// B(Cb, Cs) = SetLum(Cs, Lum(Cb))
+// Create a color with the hue and saturation of the source color and the luminosity of the backdrop color. 
+static SkPMColor color_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+    int Sr, Sg, Sb;
+
+    if(sa && da) {
+        Sr = SkMulDiv255Round(sr, sa);
+        Sg = SkMulDiv255Round(sg, sa);
+        Sb = SkMulDiv255Round(sb, sa);
+        int Dr = SkMulDiv255Round(dr, da);
+        int Dg = SkMulDiv255Round(dg, da);
+        int Db = SkMulDiv255Round(db, da);
+        SetLum(&Sr, &Sg, &Sb, Lum(Dr, Dg, Db));
+    } else {
+        Sr = 0;
+        Sg = 0;
+        Sb = 0;
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Sb);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kLuminosity_Mode
+// B(Cb, Cs) = SetLum(Cb, Lum(Cs))
+// Create a color with the luminosity of the source color and the hue and saturation of the backdrop color. 
+static SkPMColor luminosity_modeproc(SkPMColor src, SkPMColor dst) {
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    int sa = SkGetPackedA32(src);
+
+    int dr = SkGetPackedR32(dst);
+    int dg = SkGetPackedG32(dst);
+    int db = SkGetPackedB32(dst);
+    int da = SkGetPackedA32(dst);
+    int Dr, Dg, Db;
+
+    if(sa && da) {
+        int Sr = SkMulDiv255Round(sr, sa);
+        int Sg = SkMulDiv255Round(sg, sa);
+        int Sb = SkMulDiv255Round(sb, sa);
+        Dr = SkMulDiv255Round(dr, da);
+        Dg = SkMulDiv255Round(dg, da);
+        Db = SkMulDiv255Round(db, da);
+        SetLum(&Dr, &Dg, &Db, Lum(Sr, Sg, Sb));
+    } else {
+        Dr = 0;
+        Dg = 0;
+        Db = 0;
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Db);
+    return SkPackARGB32(a, r, g, b);
+}
+
+
 struct ProcCoeff {
     SkXfermodeProc      fProc;
     SkXfermode::Coeff   fSC;
@@ -466,6 +693,10 @@
     { difference_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { exclusion_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { multiply_modeproc,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { hue_modeproc,         CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { saturation_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { color_modeproc,       CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { luminosity_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1264,6 +1495,11 @@
     { NULL,                 NULL,                   NULL            }, // softlight
     { NULL,                 NULL,                   NULL            }, // difference
     { NULL,                 NULL,                   NULL            }, // exclusion
+    { NULL,                 NULL,                   NULL            }, // multiply
+    { NULL,                 NULL,                   NULL            }, // hue
+    { NULL,                 NULL,                   NULL            }, // saturation
+    { NULL,                 NULL,                   NULL            }, // color
+    { NULL,                 NULL,                   NULL            }, // luminosity
 };
 
 SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 558deb7..580d7e9 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -14,7 +14,9 @@
 static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
     switch (mode) {
         case SkXfermode::kSrcOver_Mode:    return "Normal";
-        case SkXfermode::kModulate_Mode:   return "Multiply";
+        // kModulate is not really like multipy but similar most of the time.
+        case SkXfermode::kModulate_Mode: 
+        case SkXfermode::kMultiply_Mode:   return "Multiply";
         case SkXfermode::kScreen_Mode:     return "Screen";
         case SkXfermode::kOverlay_Mode:    return "Overlay";
         case SkXfermode::kDarken_Mode:     return "Darken";
@@ -25,6 +27,10 @@
         case SkXfermode::kSoftLight_Mode:  return "SoftLight";
         case SkXfermode::kDifference_Mode: return "Difference";
         case SkXfermode::kExclusion_Mode:  return "Exclusion";
+        case SkXfermode::kHue_Mode:        return "Hue";
+        case SkXfermode::kSaturation_Mode: return "Saturation";
+        case SkXfermode::kColor_Mode:      return "Color";
+        case SkXfermode::kLuminosity_Mode: return "Luminosity";
 
         // These are handled in SkPDFDevice::setUpContentEntry.
         case SkXfermode::kClear_Mode:
@@ -42,7 +48,6 @@
         case SkXfermode::kDstATop_Mode:
         case SkXfermode::kXor_Mode:
         case SkXfermode::kPlus_Mode:
-        case SkXfermode::kMultiply_Mode:
             return NULL;
     }
     return NULL;