[PDF] Add support for xfermodes / blend modes.

- Change SkGraphicState to track and set the blend mode (xfermode) for modes built in to PDF (non porter duff modes + src over).
- Add SkXfermode::asMode() to retrieve xfermode as an enum for non porter duff modes.
- Move SkXfermode.cpp around a bit to support asMode() -- Generally move utility functions toward the top of the file.
- Make SkPDFFormXObject an isolated transparency group, as it used for saveLayer, which draws on transparent, not the device background.
- Set the graphic state in drawDevice and drawBitmap in order to get the right xfermode and alpha.

Review URL: http://codereview.appspot.com/4131043

git-svn-id: http://skia.googlecode.com/svn/trunk@774 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 7a06467..948d1dd 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -115,6 +115,13 @@
         kLastMode = kExclusion_Mode
     };
 
+    /** If the xfermode is one of the modes in the Mode enum, then asMode()
+        returns true and sets (if not null) mode accordingly.
+        This is a better version of IsMode(), which is only able to report
+        about modes that are expressible as coefficients.
+     */
+    virtual bool asMode(Mode* mode);
+
     /** Return an SkXfermode object for the specified mode.
      */
     static SkXfermode* Create(Mode mode);
@@ -182,6 +189,7 @@
     // overrides from SkFlattenable
     virtual Factory getFactory() { return CreateProc; }
     virtual void    flatten(SkFlattenableWriteBuffer&);
+    virtual bool asMode(SkXfermode::Mode* mode);
 
 protected:
     SkProcXfermode(SkFlattenableReadBuffer&);
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 8d1531a..71482c1 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -83,288 +83,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
-    return false;
-}
-
-SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
-    // no-op. subclasses should override this
-    return dst;
-}
-
-void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
-                        const SK_RESTRICT SkPMColor src[], int count,
-                        const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-
-    if (NULL == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            dst[i] = this->xferColor(src[i], dst[i]);
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkPMColor dstC = dst[i];
-                SkPMColor C = this->xferColor(src[i], dstC);
-                if (0xFF != a) {
-                    C = SkFourByteInterp(C, dstC, a);
-                }
-                dst[i] = C;
-            }
-        }
-    }
-}
-
-void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[],
-                        const SK_RESTRICT SkPMColor src[], int count,
-                        const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-
-    if (NULL == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-            dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                SkPMColor C = this->xferColor(src[i], dstC);
-                if (0xFF != a) {
-                    C = SkFourByteInterp(C, dstC, a);
-                }
-                dst[i] = SkPixel32ToPixel16_ToU16(C);
-            }
-        }
-    }
-}
-
-void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
-                          const SK_RESTRICT SkPMColor src[], int count,
-                          const SK_RESTRICT SkAlpha aa[])
-{
-    SkASSERT(dst && src && count >= 0);
-    
-    if (NULL == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
-            dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
-                SkPMColor C = this->xferColor(src[i], dstC);
-                if (0xFF != a) {
-                    C = SkFourByteInterp(C, dstC, a);
-                }
-                dst[i] = SkPixel32ToPixel4444(C);
-            }
-        }
-    }
-}
-
-void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
-                        const SkPMColor src[], int count,
-                        const SK_RESTRICT SkAlpha aa[])
-{
-    SkASSERT(dst && src && count >= 0);
-
-    if (NULL == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
-            dst[i] = SkToU8(SkGetPackedA32(res));
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkAlpha dstA = dst[i];
-                unsigned A = SkGetPackedA32(this->xferColor(src[i],
-                                            (SkPMColor)(dstA << SK_A32_SHIFT)));
-                if (0xFF != a) {
-                    A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
-                }
-                dst[i] = SkToU8(A);
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
-                            const SK_RESTRICT SkPMColor src[], int count,
-                            const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-
-    SkXfermodeProc proc = fProc;
-
-    if (NULL != proc) {
-        if (NULL == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                dst[i] = proc(src[i], dst[i]);
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkPMColor dstC = dst[i];
-                    SkPMColor C = proc(src[i], dstC);
-                    if (a != 0xFF) {
-                        C = SkFourByteInterp(C, dstC, a);
-                    }
-                    dst[i] = C;
-                }
-            }
-        }
-    }
-}
-
-void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[],
-                            const SK_RESTRICT SkPMColor src[], int count,
-                            const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-
-    SkXfermodeProc proc = fProc;
-
-    if (NULL != proc) {
-        if (NULL == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                    SkPMColor C = proc(src[i], dstC);
-                    if (0xFF != a) {
-                        C = SkFourByteInterp(C, dstC, a);
-                    }
-                    dst[i] = SkPixel32ToPixel16_ToU16(C);
-                }
-            }
-        }
-    }
-}
-
-void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
-                              const SK_RESTRICT SkPMColor src[], int count,
-                              const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-    
-    SkXfermodeProc proc = fProc;
-
-    if (NULL != proc) {
-        if (NULL == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
-                dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
-                    SkPMColor C = proc(src[i], dstC);
-                    if (0xFF != a) {
-                        C = SkFourByteInterp(C, dstC, a);
-                    }
-                    dst[i] = SkPixel32ToPixel4444(C);
-                }
-            }
-        }
-    }
-}
-
-void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
-                            const SK_RESTRICT SkPMColor src[], int count,
-                            const SK_RESTRICT SkAlpha aa[]) {
-    SkASSERT(dst && src && count >= 0);
-
-    SkXfermodeProc proc = fProc;
-
-    if (NULL != proc) {
-        if (NULL == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
-                dst[i] = SkToU8(SkGetPackedA32(res));
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkAlpha dstA = dst[i];
-                    SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
-                    unsigned A = SkGetPackedA32(res);
-                    if (0xFF != a) {
-                        A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
-                    }
-                    dst[i] = SkToU8(A);
-                }
-            }
-        }
-    }
-}
-
-SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
-        : SkXfermode(buffer) {
-    fProc = (SkXfermodeProc)buffer.readFunctionPtr();
-}
-
-void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
-    buffer.writeFunctionPtr((void*)fProc);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-class SkProcCoeffXfermode : public SkProcXfermode {
-public:
-    SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc)
-            : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) {
-    }
-    
-    virtual bool asCoeff(Coeff* sc, Coeff* dc) {
-        if (sc) {
-            *sc = fSrcCoeff;
-        }
-        if (dc) {
-            *dc = fDstCoeff;
-        }
-        return true;
-    }
-    
-    virtual Factory getFactory() { return CreateProc; }
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        buffer.write32(fSrcCoeff);
-        buffer.write32(fDstCoeff);
-    }
-
-protected:
-    SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer)
-            : INHERITED(buffer) {
-        fSrcCoeff = (Coeff)buffer.readU32();
-        fDstCoeff = (Coeff)buffer.readU32();
-    }
-    
-private:
-    Coeff   fSrcCoeff, fDstCoeff;
-    
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); }
-
-    typedef SkProcXfermode INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
 //  kClear_Mode,    //!< [0, 0]
 static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
     return 0;
@@ -708,6 +426,340 @@
     return SkPackARGB32(a, r, g, b);
 }
 
+struct ProcCoeff {
+    SkXfermodeProc      fProc;
+    SkXfermode::Coeff   fSC;
+    SkXfermode::Coeff   fDC;
+};
+
+#define CANNOT_USE_COEFF    SkXfermode::Coeff(-1)
+
+static const ProcCoeff gProcCoeffs[] = {
+    { clear_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
+    { src_modeproc,     SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
+    { dst_modeproc,     SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff },
+    { srcover_modeproc, SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff },
+    { dstover_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff },
+    { srcin_modeproc,   SkXfermode::kDA_Coeff,      SkXfermode::kZero_Coeff },
+    { 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,        CANNOT_USE_COEFF,       CANNOT_USE_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 },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+    return false;
+}
+
+bool SkXfermode::asMode(Mode* mode) {
+    return IsMode(this, mode);
+}
+
+SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+    // no-op. subclasses should override this
+    return dst;
+}
+
+void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            dst[i] = this->xferColor(src[i], dst[i]);
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = dst[i];
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = C;
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel16_ToU16(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+                          const SK_RESTRICT SkPMColor src[], int count,
+                          const SK_RESTRICT SkAlpha aa[])
+{
+    SkASSERT(dst && src && count >= 0);
+    
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel4444(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+                        const SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[])
+{
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
+            dst[i] = SkToU8(SkGetPackedA32(res));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkAlpha dstA = dst[i];
+                unsigned A = SkGetPackedA32(this->xferColor(src[i],
+                                            (SkPMColor)(dstA << SK_A32_SHIFT)));
+                if (0xFF != a) {
+                    A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                }
+                dst[i] = SkToU8(A);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                dst[i] = proc(src[i], dst[i]);
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = dst[i];
+                    SkPMColor C = proc(src[i], dstC);
+                    if (a != 0xFF) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = C;
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel16_ToU16(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+                              const SK_RESTRICT SkPMColor src[], int count,
+                              const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+    
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel4444(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
+                dst[i] = SkToU8(SkGetPackedA32(res));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkAlpha dstA = dst[i];
+                    SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
+                    unsigned A = SkGetPackedA32(res);
+                    if (0xFF != a) {
+                        A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                    }
+                    dst[i] = SkToU8(A);
+                }
+            }
+        }
+    }
+}
+
+SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
+        : SkXfermode(buffer) {
+    fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+}
+
+void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeFunctionPtr((void*)fProc);
+}
+
+bool SkProcXfermode::asMode(SkXfermode::Mode* mode) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gProcCoeffs); i++) {
+        if (gProcCoeffs[i].fProc == fProc) {
+            if (mode) {
+                *mode = static_cast<Mode>(i);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class SkProcCoeffXfermode : public SkProcXfermode {
+public:
+    SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc)
+            : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) {
+    }
+    
+    virtual bool asCoeff(Coeff* sc, Coeff* dc) {
+        if (sc) {
+            *sc = fSrcCoeff;
+        }
+        if (dc) {
+            *dc = fDstCoeff;
+        }
+        return true;
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.write32(fSrcCoeff);
+        buffer.write32(fDstCoeff);
+    }
+
+protected:
+    SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer)
+            : INHERITED(buffer) {
+        fSrcCoeff = (Coeff)buffer.readU32();
+        fDstCoeff = (Coeff)buffer.readU32();
+    }
+    
+private:
+    Coeff   fSrcCoeff, fDstCoeff;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); }
+
+    typedef SkProcXfermode INHERITED;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkClearXfermode : public SkProcCoeffXfermode {
@@ -901,42 +953,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-struct ProcCoeff {
-    SkXfermodeProc      fProc;
-    SkXfermode::Coeff   fSC;
-    SkXfermode::Coeff   fDC;
-};
-
-#define CANNOT_USE_COEFF    SkXfermode::Coeff(-1)
-
-static const ProcCoeff gProcCoeffs[] = {
-    { clear_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
-    { src_modeproc,     SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
-    { dst_modeproc,     SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff },
-    { srcover_modeproc, SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff },
-    { dstover_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff },
-    { srcin_modeproc,   SkXfermode::kDA_Coeff,      SkXfermode::kZero_Coeff },
-    { 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,        CANNOT_USE_COEFF,       CANNOT_USE_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 },
-};
-
 SkXfermode* SkXfermode::Create(Mode mode) {
     SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
     SkASSERT((unsigned)mode < kModeCount);
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 9ebc0ca..8a74556 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -132,7 +132,7 @@
 
 SkPDFDevice::SkPDFDevice(int width, int height)
     : SkDevice(NULL, makeABitmap(width, height), false),
-	  fWidth(width),
+      fWidth(width),
       fHeight(height),
       fGraphicStackIndex(0) {
     fGraphicStack[0].fColor = SK_ColorBLACK;
@@ -441,19 +441,18 @@
         return;
     }
     // Assume that a vector capable device means that it's a PDF Device.
-    // TODO(vandebo) handle the paint (alpha and compositing mode).
     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
 
     SkMatrix matrix;
     matrix.setTranslate(x, y);
     SkMatrix curTransform = setTransform(matrix);
+    updateGSFromPaint(paint, false);
 
     SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
     fXObjectResources.push(xobject);  // Transfer reference.
     fContent.append("/X");
     fContent.appendS32(fXObjectResources.count() - 1);
     fContent.append(" Do\n");
-
     setTransform(curTransform);
 }
 
@@ -567,8 +566,8 @@
 #define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false)
 
 void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
-    PAINTCHECK(getXfermode, != NULL);
-    PAINTCHECK(getPathEffect, != NULL);
+    SkASSERT(newPaint.getPathEffect() == NULL);
+
     PAINTCHECK(getMaskFilter, != NULL);
     PAINTCHECK(getShader, != NULL);
     PAINTCHECK(getColorFilter, != NULL);
@@ -817,6 +816,7 @@
     scaled.postScale(subset.width(), subset.height());
     scaled.postConcat(matrix);
     SkMatrix curTransform = setTransform(scaled);
+    updateGSFromPaint(paint, false);
 
     fXObjectResources.push(image);  // Transfer reference.
     fContent.append("/X");
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index 346bce6..ef839a3 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -39,6 +39,14 @@
     insert("Subtype", new SkPDFName("Form"))->unref();
     insert("BBox", device->getMediaBox().get());
     insert("Resources", device->getResourceDict().get());
+
+    // Right now SkPDFFormXObject is only used for saveLayer, which implies
+    // isolated blending.  Do this conditionally if that changes.
+    SkRefPtr<SkPDFDict> group = new SkPDFDict("Group");
+    group->unref();  // SkRefPtr and new both took a reference.
+    group->insert("S", new SkPDFName("Transparency"))->unref();
+    group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
+    insert("Group", group.get());
 }
 
 SkPDFFormXObject::~SkPDFFormXObject() {
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index ff55941..44419ec 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -16,6 +16,44 @@
 
 #include "SkPDFGraphicState.h"
 #include "SkStream.h"
+#include "SkTypes.h"
+
+namespace {
+
+const char* blendModeFromXfermode(SkXfermode::Mode mode) {
+    switch (mode) {
+        case SkXfermode::kSrcOver_Mode:    return "Normal";
+        case SkXfermode::kMultiply_Mode:   return "Multiply";
+        case SkXfermode::kScreen_Mode:     return "Screen";
+        case SkXfermode::kOverlay_Mode:    return "Overlay";
+        case SkXfermode::kDarken_Mode:     return "Darken";
+        case SkXfermode::kLighten_Mode:    return "Lighten";
+        case SkXfermode::kColorDodge_Mode: return "ColorDodge";
+        case SkXfermode::kColorBurn_Mode:  return "ColorBurn";
+        case SkXfermode::kHardLight_Mode:  return "HardLight";
+        case SkXfermode::kSoftLight_Mode:  return "SoftLight";
+        case SkXfermode::kDifference_Mode: return "Difference";
+        case SkXfermode::kExclusion_Mode:  return "Exclusion";
+
+        // TODO(vandebo) Figure out if we can support more of these modes.
+        case SkXfermode::kClear_Mode:
+        case SkXfermode::kSrc_Mode:
+        case SkXfermode::kDst_Mode:
+        case SkXfermode::kDstOver_Mode:
+        case SkXfermode::kSrcIn_Mode:
+        case SkXfermode::kDstIn_Mode:
+        case SkXfermode::kSrcOut_Mode:
+        case SkXfermode::kDstOut_Mode:
+        case SkXfermode::kSrcATop_Mode:
+        case SkXfermode::kDstATop_Mode:
+        case SkXfermode::kXor_Mode:
+        case SkXfermode::kPlus_Mode:
+            return NULL;
+    }
+    return NULL;
+}
+
+}
 
 SkPDFGraphicState::~SkPDFGraphicState() {
     SkAutoMutexAcquire lock(canonicalPaintsMutex());
@@ -30,6 +68,7 @@
     SkPDFDict::emitObject(stream, catalog, indirect);
 }
 
+// static
 size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
     populateDict();
     return SkPDFDict::getOutputSize(catalog, indirect);
@@ -104,6 +143,18 @@
         insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
         insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
         insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
+
+        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+        // If asMode fails return false, default to kSrcOver_Mode.
+        if (fPaint.getXfermode())
+            fPaint.getXfermode()->asMode(&xfermode);
+        // If we don't support the mode, just use kSrcOver_Mode.
+        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
+                blendModeFromXfermode(xfermode) == NULL) {
+            fprintf(stderr, "NOT_IMPLEMENTED: xfermode = %d\n", xfermode);
+            xfermode = SkXfermode::kSrcOver_Mode;
+        }
+        insert("BM", new SkPDFName(blendModeFromXfermode(xfermode)))->unref();
     }
 }
 
@@ -115,9 +166,29 @@
     const SkPaint* b = gs.fPaint;
     SkASSERT(a != NULL);
     SkASSERT(b != NULL);
-    return SkColorGetA(a->getColor()) == SkColorGetA(b->getColor()) &&
-           a->getStrokeCap() == b->getStrokeCap() &&
-           a->getStrokeJoin() == b->getStrokeJoin() &&
-           a->getStrokeWidth() == b->getStrokeWidth() &&
-           a->getStrokeMiter() == b->getStrokeMiter();
+
+    if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
+           a->getStrokeCap() != b->getStrokeCap() ||
+           a->getStrokeJoin() != b->getStrokeJoin() ||
+           a->getStrokeWidth() != b->getStrokeWidth() ||
+           a->getStrokeMiter() != b->getStrokeMiter()) {
+        return false;
+    }
+
+    SkXfermode* aXfermode = a->getXfermode();
+    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
+    bool aXfermodeKnown = true;
+    if (aXfermode)
+        aXfermodeKnown = aXfermode->asMode(&aXfermodeName);
+    SkXfermode* bXfermode = b->getXfermode();
+    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
+    bool bXfermodeKnown = true;
+    if (bXfermode)
+        bXfermodeKnown = bXfermode->asMode(&bXfermodeName);
+
+    if (aXfermodeKnown != bXfermodeKnown)
+        return false;
+    if (!aXfermodeKnown)
+       return aXfermode == bXfermode;
+    return aXfermodeName == bXfermodeName;
 }
diff --git a/tests/XfermodeTest.cpp b/tests/XfermodeTest.cpp
new file mode 100644
index 0000000..6ab6cd7
--- /dev/null
+++ b/tests/XfermodeTest.cpp
@@ -0,0 +1,30 @@
+#include "Test.h"
+#include "SkColor.h"
+#include "SkXfermode.h"
+
+SkPMColor bogusXfermodeProc(SkPMColor src, SkPMColor dst) {
+    return 42;
+}
+
+static void test_asMode(skiatest::Reporter* reporter) {
+    for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
+        SkXfermode* xfer = SkXfermode::Create((SkXfermode::Mode) mode);
+        SkXfermode::Mode reportedMode = (SkXfermode::Mode) -1;
+        REPORTER_ASSERT(reporter,
+                        xfer != NULL || mode == SkXfermode::kSrcOver_Mode);
+        if (xfer) {
+          REPORTER_ASSERT(reporter, xfer->asMode(&reportedMode));
+          REPORTER_ASSERT(reporter, reportedMode == mode);
+          xfer->unref();
+        }
+    }
+
+    SkXfermode* bogusXfer = new SkProcXfermode(bogusXfermodeProc);
+    SkXfermode::Mode reportedMode = (SkXfermode::Mode) -1;
+    REPORTER_ASSERT(reporter, !bogusXfer->asMode(&reportedMode));
+    REPORTER_ASSERT(reporter, reportedMode == -1);
+    bogusXfer->unref();
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Xfermode", XfermodeTestClass, test_asMode)
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
index 4aa7cc0..1c6a0ef 100644
--- a/tests/tests_files.mk
+++ b/tests/tests_files.mk
@@ -6,6 +6,7 @@
     FillPathTest.cpp \
     FlateTest.cpp \
     GeometryTest.cpp \
+    InfRectTest.cpp \
     MathTest.cpp \
     MatrixTest.cpp \
     PackBitsTest.cpp \
@@ -24,4 +25,4 @@
     Test.cpp \
     TestSize.cpp \
     UtilsTest.cpp \
-    InfRectTest.cpp
+    XfermodeTest.cpp