# HG changeset patch
# User Rik Cabanier <cabanier@adobe.com>
# Date 1360273929 -46800
# Node ID 3ac8edca3a03b3d22240b5a5b95ae3b5ada9877d
# Parent  cbb67fe70b864b36165061e1fd3b083cd09af087
Bug 836892 - Add new blending modes to SkXfermode. r=gw280

diff --git a/gfx/skia/include/core/SkXfermode.h b/gfx/skia/include/core/SkXfermode.h
--- a/gfx/skia/include/core/SkXfermode.h
+++ b/gfx/skia/include/core/SkXfermode.h
@@ -96,33 +96,37 @@ public:
         kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
         kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
         kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
 
         // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode,
 
         // all above modes can be expressed as pair of src/dst Coeffs
         kCoeffModesCnt,
 
-        kScreen_Mode = kCoeffModesCnt,
+        kMultiply_Mode = kCoeffModesCnt,
+        kScreen_Mode,
         kOverlay_Mode,
         kDarken_Mode,
         kLighten_Mode,
         kColorDodge_Mode,
         kColorBurn_Mode,
         kHardLight_Mode,
         kSoftLight_Mode,
         kDifference_Mode,
         kExclusion_Mode,
+        kHue_Mode,
+        kSaturation_Mode,
+        kColor_Mode,
+        kLuminosity_Mode,
 
-        kLastMode = kExclusion_Mode
+        kLastMode = kLuminosity_Mode
     };
 
     /**
      *  If the xfermode is one of the modes in the Mode enum, then asMode()
      *  returns true and sets (if not null) mode accordingly. Otherwise it
      *  returns false and ignores the mode parameter.
      */
     virtual bool asMode(Mode* mode);
diff --git a/gfx/skia/src/core/SkXfermode.cpp b/gfx/skia/src/core/SkXfermode.cpp
--- a/gfx/skia/src/core/SkXfermode.cpp
+++ b/gfx/skia/src/core/SkXfermode.cpp
@@ -7,16 +7,18 @@
  */
 
 
 #include "SkXfermode.h"
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
 #include "SkMathPriv.h"
 
+#include <algorithm>
+
 SK_DEFINE_INST_COUNT(SkXfermode)
 
 #define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
 
 #if 0
 // idea for higher precision blends in xfer procs (and slightly faster)
 // see DstATop as a probable caller
 static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
@@ -176,244 +178,439 @@ static SkPMColor xor_modeproc(SkPMColor 
 static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
     unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
     unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
     unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
     unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
     return SkPackARGB32(a, r, g, b);
 }
 
+static inline int srcover_byte(int a, int b) {
+    return a + b - SkAlphaMulAlpha(a, b);
+}
+
+#define  blendfunc_byte(sc, dc, sa, da, blendfunc) \
+  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  + blendfunc(sc, dc, sa, da))
+
 // kMultiply_Mode
+static inline int multiply_byte(int sc, int dc, int sa, int da) {
+    return sc * dc;
+}
 static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
-    int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
-    int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
-    int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
-    int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, multiply_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, multiply_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, multiply_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kScreen_Mode
-static inline int srcover_byte(int a, int b) {
-    return a + b - SkAlphaMulAlpha(a, b);
+static inline int screen_byte(int sc, int dc, int sa, int da) {
+    return sc * da + sa * dc - sc * dc;
 }
 static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
-    int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
-    int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
-    int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
-    int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, screen_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, screen_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, screen_byte);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kHardLight_Mode
+static inline int hardlight_byte(int sc, int dc, int sa, int da) {
+    if(!sa || !da)
+        return sc * da;
+    float Sc = (float)sc/sa;
+    float Dc = (float)dc/da;
+    if(Sc <= 0.5)
+        Sc *= 2 * Dc;
+    else
+        Sc = -1 + 2 * Sc + 2 * Dc - 2 * Sc * Dc;
+
+    return Sc * sa * da;
+}
+static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, hardlight_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, hardlight_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, hardlight_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kOverlay_Mode
 static inline int overlay_byte(int sc, int dc, int sa, int da) {
-    int tmp = sc * (255 - da) + dc * (255 - sa);
-    int rc;
-    if (2 * dc <= da) {
-        rc = 2 * sc * dc;
-    } else {
-        rc = sa * da - 2 * (da - dc) * (sa - sc);
-    }
-    return clamp_div255round(rc + tmp);
+    return hardlight_byte(dc, sc, da, sa);
 }
 static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, overlay_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, overlay_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, overlay_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kDarken_Mode
 static inline int darken_byte(int sc, int dc, int sa, int da) {
-    int sd = sc * da;
-    int ds = dc * sa;
-    if (sd < ds) {
-        // srcover
-        return sc + dc - SkDiv255Round(ds);
-    } else {
-        // dstover
-        return dc + sc - SkDiv255Round(sd);
-    }
+    return SkMin32(sc * da, sa * dc);
 }
 static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, darken_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, darken_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, darken_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kLighten_Mode
 static inline int lighten_byte(int sc, int dc, int sa, int da) {
-    int sd = sc * da;
-    int ds = dc * sa;
-    if (sd > ds) {
-        // srcover
-        return sc + dc - SkDiv255Round(ds);
-    } else {
-        // dstover
-        return dc + sc - SkDiv255Round(sd);
-    }
+    return SkMax32(sc * da, sa * dc);
 }
 static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, lighten_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, lighten_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, lighten_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kColorDodge_Mode
 static inline int colordodge_byte(int sc, int dc, int sa, int da) {
-    int diff = sa - sc;
-    int rc;
-    if (0 == diff) {
-        rc = sa * da + sc * (255 - da) + dc * (255 - sa);
-        rc = SkDiv255Round(rc);
-    } else {
-        int tmp = (dc * sa << 15) / (da * diff);
-        rc = SkDiv255Round(sa * da) * tmp >> 15;
-        // don't clamp here, since we'll do it in our modeproc
-    }
-    return rc;
+    if (dc == 0)
+        return 0;
+    // Avoid division by 0
+    if (sc == sa)
+        return da * sa;
+
+    return SkMin32(sa * da, sa * sa * dc / (sa - sc));
 }
 static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
-    // added to avoid div-by-zero in colordodge_byte
-    if (0 == dst) {
-        return src;
-    }
-
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
-    r = clamp_max(r, a);
-    g = clamp_max(g, a);
-    b = clamp_max(b, a);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colordodge_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colordodge_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colordodge_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kColorBurn_Mode
 static inline int colorburn_byte(int sc, int dc, int sa, int da) {
-    int rc;
-    if (dc == da && 0 == sc) {
-        rc = sa * da + dc * (255 - sa);
-    } else if (0 == sc) {
-        return SkAlphaMulAlpha(dc, 255 - sa);
-    } else {
-        int tmp = (sa * (da - dc) * 256) / (sc * da);
-        if (tmp > 256) {
-            tmp = 256;
-        }
-        int tmp2 = sa * da;
-        rc = tmp2 - (tmp2 * tmp >> 8) + sc * (255 - da) + dc * (255 - sa);
-    }
-    return SkDiv255Round(rc);
+    // Avoid division by 0
+    if(dc == da)
+        return sa * da;
+    if(sc == 0)
+        return 0;
+
+    return sa * da - SkMin32(sa * da, sa * sa * (da - dc) / sc);
 }
 static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
-    // added to avoid div-by-zero in colorburn_byte
-    if (0 == dst) {
-        return src;
-    }
-
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
-    return SkPackARGB32(a, r, g, b);
-}
-
-// kHardLight_Mode
-static inline int hardlight_byte(int sc, int dc, int sa, int da) {
-    int rc;
-    if (2 * sc <= sa) {
-        rc = 2 * sc * dc;
-    } else {
-        rc = sa * da - 2 * (da - dc) * (sa - sc);
-    }
-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
-}
-static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
-    int sa = SkGetPackedA32(src);
-    int da = SkGetPackedA32(dst);
-    int a = srcover_byte(sa, da);
-    int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, colorburn_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, colorburn_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, colorburn_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // returns 255 * sqrt(n/255)
 static U8CPU sqrt_unit_byte(U8CPU n) {
     return SkSqrtBits(n, 15+4);
 }
 
 // kSoftLight_Mode
 static inline int softlight_byte(int sc, int dc, int sa, int da) {
     int m = da ? dc * 256 / da : 0;
     int rc;
-    if (2 * sc <= sa) {
-        rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
-    } else if (4 * dc <= da) {
+    if (2 * sc <= sa)
+       return dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
+
+    if (4 * dc <= da) {
         int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
-    } else {
-        int tmp = sqrt_unit_byte(m) - m;
-        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+        return dc * sa + (da * (2 * sc - sa) * tmp >> 8);
     }
-    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
+    int tmp = sqrt_unit_byte(m) - m;
+    return rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
 }
 static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, softlight_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, softlight_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, softlight_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kDifference_Mode
 static inline int difference_byte(int sc, int dc, int sa, int da) {
-    int tmp = SkMin32(sc * da, dc * sa);
-    return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
+    int tmp = dc * sa - sc * da;
+    if(tmp<0)
+        return - tmp;
+
+    return tmp;
 }
 static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, difference_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, difference_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, difference_byte);
     return SkPackARGB32(a, r, g, b);
 }
 
 // kExclusion_Mode
 static inline int exclusion_byte(int sc, int dc, int sa, int da) {
-    // this equations is wacky, wait for SVG to confirm it
-    int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
-    return clamp_div255round(r);
+    return sc * da + dc * sa - 2 * dc * sc;
 }
 static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
     int sa = SkGetPackedA32(src);
     int da = SkGetPackedA32(dst);
     int a = srcover_byte(sa, da);
-    int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
-    int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
-    int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    int r = blendfunc_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da, exclusion_byte);
+    int g = blendfunc_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da, exclusion_byte);
+    int b = blendfunc_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da, exclusion_byte);
+    return SkPackARGB32(a, r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct BlendColor {
+    float r;
+    float g;
+    float b;
+
+    BlendColor(): r(0), g(0), b(0)
+      {}
+};
+
+static inline float Lum(BlendColor C)
+{
+    return C.r * 0.3 + C.g * 0.59 + C.b* 0.11;
+}
+
+static inline float SkMinFloat(float a, float b)
+{
+  if (a > b)
+    a = b;
+  return a;
+}
+
+static inline float SkMaxFloat(float a, float b)
+{
+  if (a < b)
+    a = b;
+  return a;
+}
+
+#define minimum(C) SkMinFloat(SkMinFloat(C.r, C.g), C.b)
+#define maximum(C) SkMaxFloat(SkMaxFloat(C.r, C.g), C.b)
+
+static inline float Sat(BlendColor c) {
+    return maximum(c) - minimum(c);
+}
+
+static inline void setSaturationComponents(float& Cmin, float& Cmid, float& Cmax, float s) {
+    if(Cmax > Cmin) {
+        Cmid =  (((Cmid - Cmin) * s ) / (Cmax - Cmin));
+        Cmax = s;
+    } else {
+        Cmax = 0;
+        Cmid = 0;
+    }
+    Cmin = 0;
+}
+
+static inline BlendColor SetSat(BlendColor C, float s) {
+    if(C.r <= C.g) {
+        if(C.g <= C.b)
+            setSaturationComponents(C.r, C.g, C.b, s);
+        else
+        if(C.r <= C.b)
+            setSaturationComponents(C.r, C.b, C.g, s);
+        else
+            setSaturationComponents(C.b, C.r, C.g, s);
+        } else if(C.r <= C.b)
+            setSaturationComponents(C.g, C.r, C.b, s);
+        else
+        if(C.g <= C.b)
+            setSaturationComponents(C.g, C.b, C.r, s);
+        else
+            setSaturationComponents(C.b, C.g, C.r, s);
+
+        return C;
+}
+
+static inline BlendColor clipColor(BlendColor C) {
+    float L = Lum(C);
+    float n = minimum(C);
+    float x = maximum(C);
+    if(n < 0) {
+       C.r = L + (((C.r - L) * L) / (L - n));
+       C.g = L + (((C.g - L) * L) / (L - n));
+       C.b = L + (((C.b - L) * L) / (L - n));
+    }
+
+    if(x > 1) {
+       C.r = L + (((C.r - L) * (1 - L)) / (x - L));
+       C.g = L + (((C.g - L) * (1 - L)) / (x - L));
+       C.b = L + (((C.b - L) * (1 - L)) / (x - L));
+    }
+    return C;
+}
+
+static inline BlendColor SetLum(BlendColor C, float l) {
+  float d = l - Lum(C);
+  C.r +=  d;
+  C.g +=  d;
+  C.b +=  d;
+
+  return clipColor(C);
+}
+
+#define  blendfunc_nonsep_byte(sc, dc, sa, da, blendval) \
+  clamp_div255round(sc * (255 - da)  + dc * (255 - sa)  +  (int)(sa * da * blendval))
+
+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);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(SetSat(Cs, Sat(Cd)), Lum(Cd));
+        }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+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);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(SetSat(Cd, Sat(Cs)), Lum(Cd));
+        }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+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);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(Cs, Lum(Cd));
+            }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
+    return SkPackARGB32(a, r, g, b);
+}
+
+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);
+
+    BlendColor Cs;
+    if(sa) {
+        Cs.r  = (float)sr / sa;
+        Cs.g = (float)sg / sa;
+        Cs.b = (float)sb / sa;
+        BlendColor Cd;
+        if(da) {
+            Cd.r =  (float)dr / da;
+            Cd.g = (float)dg / da;
+            Cd.b = (float)db / da;
+            Cs = SetLum(Cd, Lum(Cs));
+            }
+    }
+
+    int a = srcover_byte(sa, da);
+    int r = blendfunc_nonsep_byte(sr, dr, sa, da, Cs.r);
+    int g = blendfunc_nonsep_byte(sg, dg, sa, da, Cs.g);
+    int b = blendfunc_nonsep_byte(sb, db, sa, da, Cs.b);
     return SkPackARGB32(a, r, g, b);
 }
 
 struct ProcCoeff {
     SkXfermodeProc      fProc;
     SkXfermode::Coeff   fSC;
     SkXfermode::Coeff   fDC;
 };
@@ -430,27 +627,31 @@ static const ProcCoeff gProcCoeffs[] = {
     { dstin_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff },
     { srcout_modeproc,  SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
     { dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
     { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
     { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
 
     { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
-    { multiply_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
+    { multiply_modeproc,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF},
     { screen_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { overlay_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { darken_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { lighten_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { colordodge_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { colorburn_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { hardlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { softlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { difference_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { exclusion_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 },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
     return false;
 }
 
@@ -1172,16 +1373,20 @@ static const Proc16Rec gModeProcs16[] = 
     { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
     { lighten_modeproc16_0, lighten_modeproc16_255, NULL            }, // lighten
     { NULL,                 NULL,                   NULL            }, // colordodge
     { NULL,                 NULL,                   NULL            }, // colorburn
     { NULL,                 NULL,                   NULL            }, // hardlight
     { NULL,                 NULL,                   NULL            }, // softlight
     { NULL,                 NULL,                   NULL            }, // difference
     { NULL,                 NULL,                   NULL            }, // exclusion
+    { NULL,                 NULL,                   NULL            }, // hue
+    { NULL,                 NULL,                   NULL            }, // saturation
+    { NULL,                 NULL,                   NULL            }, // color
+    { NULL,                 NULL,                   NULL            }, // luminosity
 };
 
 SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
     SkXfermodeProc16  proc16 = NULL;
     if ((unsigned)mode < kModeCount) {
         const Proc16Rec& rec = gModeProcs16[mode];
         unsigned a = SkColorGetA(srcColor);