Fix drawing xfermodes to PDF.

Cherry pick of https://code.google.com/p/skia/source/detail?r=12034 in Skia
[PDF] Improve complex xfer mode support.

Xfer mode applies only to the shape of the source drawing, not everything in the
clip as in currently implemented.  It's just that the current gm examples draw a
shape that fills the visible layer.

BUG:11477060

Change-Id: I9f82aeb09ead0d7e600590264a594262ec6c6282
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
index 65f53bf..b0d7ef2 100644
--- a/gm/xfermodes.cpp
+++ b/gm/xfermodes.cpp
@@ -46,17 +46,84 @@
 static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
 
 class XfermodesGM : public GM {
+    enum SrcType {
+     //! A WxH image with a rectangle in the lower right.
+     kRectangleImage_SrcType               = 0x01,
+     //! kRectangleImage_SrcType with an alpha of 34.5%.
+     kRectangleImageWithAlpha_SrcType      = 0x02,
+     //! kRectnagleImageWithAlpha_SrcType scaled down by half.
+     kSmallRectangleImageWithAlpha_SrcType = 0x04,
+     //! kRectangleImage_SrcType drawn directly instead in an image.
+     kRectangle_SrcType                    = 0x08,
+     //! Two rectangles, first on the right half, second on the bottom half.
+     kQuarterClear_SrcType                 = 0x10,
+     //! kQuarterClear_SrcType in a layer.
+     kQuarterClearInLayer_SrcType          = 0x20,
+
+     kAll_SrcType                          = 0x3F, //!< All the source types.
+     kBasic_SrcType                        = 0x03, //!< Just basic source types.
+    };
+
     SkBitmap    fBG;
     SkBitmap    fSrcB, fDstB;
 
-    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
+    /* The srcType argument indicates what to draw for the source part. Skia
+     * uses the implied shape of the drawing command and these modes
+     * demonstrate that.
+     */
+    void draw_mode(SkCanvas* canvas, SkXfermode* mode, SrcType srcType,
                    SkScalar x, SkScalar y) {
         SkPaint p;
+        SkMatrix m;
+        bool restoreNeeded = false;
+        m.setTranslate(x, y);
 
-        canvas->drawBitmap(fSrcB, x, y, &p);
-        p.setAlpha(alpha);
+        canvas->drawBitmapMatrix(fSrcB, m, &p);
         p.setXfermode(mode);
-        canvas->drawBitmap(fDstB, x, y, &p);
+        switch (srcType) {
+            case kQuarterClearInLayer_SrcType: {
+                SkRect bounds = SkRect::MakeXYWH(x, y, W, H);
+                canvas->saveLayer(&bounds, &p);
+                restoreNeeded = true;
+                p.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+                // Fall through.
+            }
+            case kQuarterClear_SrcType: {
+                SkScalar halfW = SkIntToScalar(W) / 2;
+                SkScalar halfH = SkIntToScalar(H) / 2;
+                p.setColor(0xFF66AAFF);
+                SkRect r = SkRect::MakeXYWH(x + halfW, y, halfW, H);
+                canvas->drawRect(r, p);
+                p.setColor(0xFFAA66FF);
+                r = SkRect::MakeXYWH(x, y + halfH, W, halfH);
+                canvas->drawRect(r, p);
+                break;
+            }
+            case kRectangle_SrcType: {
+                SkScalar w = SkIntToScalar(W);
+                SkScalar h = SkIntToScalar(H);
+                SkRect r = SkRect::MakeXYWH(x + w / 3, y + h / 3,
+                                            w * 37 / 60, h * 37 / 60);
+                p.setColor(0xFF66AAFF);
+                canvas->drawRect(r, p);
+                break;
+            }
+            case kSmallRectangleImageWithAlpha_SrcType:
+                m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);
+                // Fall through.
+            case kRectangleImageWithAlpha_SrcType:
+                p.setAlpha(0x88);
+                // Fall through.
+            case kRectangleImage_SrcType:
+                canvas->drawBitmapMatrix(fDstB, m, &p);
+                break;
+            default:
+                break;
+        }
+
+        if (restoreNeeded) {
+            canvas->restore();
+        }
     }
 
     virtual void onOnceBeforeDraw() SK_OVERRIDE {
@@ -78,7 +145,7 @@
     }
 
     virtual SkISize onISize() {
-        return make_isize(790, 640);
+        return make_isize(1590, 640);
     }
 
     virtual void onDraw(SkCanvas* canvas) {
@@ -86,38 +153,44 @@
 
         const struct {
             SkXfermode::Mode  fMode;
-            const char*         fLabel;
+            const char*       fLabel;
+            int               fSourceTypeMask;  // The source types to use this
+                                                // mode with. See draw_mode for
+                                                // an explanation of each type.
+                                                // PDF has to play some tricks
+                                                // to support the base modes,
+                                                // test those more extensively.
         } gModes[] = {
-            { SkXfermode::kClear_Mode,    "Clear"     },
-            { SkXfermode::kSrc_Mode,      "Src"       },
-            { SkXfermode::kDst_Mode,      "Dst"       },
-            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
-            { SkXfermode::kDstOver_Mode,  "DstOver"   },
-            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
-            { SkXfermode::kDstIn_Mode,    "DstIn"     },
-            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
-            { SkXfermode::kDstOut_Mode,   "DstOut"    },
-            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
-            { SkXfermode::kDstATop_Mode,  "DstATop"   },
-            { SkXfermode::kXor_Mode,      "Xor"       },
+            { SkXfermode::kClear_Mode,        "Clear",        kAll_SrcType   },
+            { SkXfermode::kSrc_Mode,          "Src",          kAll_SrcType   },
+            { SkXfermode::kDst_Mode,          "Dst",          kAll_SrcType   },
+            { SkXfermode::kSrcOver_Mode,      "SrcOver",      kAll_SrcType   },
+            { SkXfermode::kDstOver_Mode,      "DstOver",      kAll_SrcType   },
+            { SkXfermode::kSrcIn_Mode,        "SrcIn",        kAll_SrcType   },
+            { SkXfermode::kDstIn_Mode,        "DstIn",        kAll_SrcType   },
+            { SkXfermode::kSrcOut_Mode,       "SrcOut",       kAll_SrcType   },
+            { SkXfermode::kDstOut_Mode,       "DstOut",       kAll_SrcType   },
+            { SkXfermode::kSrcATop_Mode,      "SrcATop",      kAll_SrcType   },
+            { SkXfermode::kDstATop_Mode,      "DstATop",      kAll_SrcType   },
 
-            { SkXfermode::kPlus_Mode,         "Plus"          },
-            { SkXfermode::kModulate_Mode,     "Modulate"      },
-            { SkXfermode::kScreen_Mode,       "Screen"        },
-            { SkXfermode::kOverlay_Mode,      "Overlay"       },
-            { SkXfermode::kDarken_Mode,       "Darken"        },
-            { SkXfermode::kLighten_Mode,      "Lighten"       },
-            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
-            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
-            { SkXfermode::kHardLight_Mode,    "HardLight"     },
-            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
-            { SkXfermode::kDifference_Mode,   "Difference"    },
-            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
-            { SkXfermode::kMultiply_Mode,     "Multiply"      },
-            { SkXfermode::kHue_Mode,          "Hue"           },
-            { SkXfermode::kSaturation_Mode,   "Saturation"    },
-            { SkXfermode::kColor_Mode,        "Color"         },
-            { SkXfermode::kLuminosity_Mode,   "Luminosity"    },
+            { SkXfermode::kXor_Mode,          "Xor",          kBasic_SrcType },
+            { SkXfermode::kPlus_Mode,         "Plus",         kBasic_SrcType },
+            { SkXfermode::kModulate_Mode,     "Modulate",     kAll_SrcType   },
+            { SkXfermode::kScreen_Mode,       "Screen",       kBasic_SrcType },
+            { SkXfermode::kOverlay_Mode,      "Overlay",      kBasic_SrcType },
+            { SkXfermode::kDarken_Mode,       "Darken",       kBasic_SrcType },
+            { SkXfermode::kLighten_Mode,      "Lighten",      kBasic_SrcType },
+            { SkXfermode::kColorDodge_Mode,   "ColorDodge",   kBasic_SrcType },
+            { SkXfermode::kColorBurn_Mode,    "ColorBurn",    kBasic_SrcType },
+            { SkXfermode::kHardLight_Mode,    "HardLight",    kBasic_SrcType },
+            { SkXfermode::kSoftLight_Mode,    "SoftLight",    kBasic_SrcType },
+            { SkXfermode::kDifference_Mode,   "Difference",   kBasic_SrcType },
+            { SkXfermode::kExclusion_Mode,    "Exclusion",    kBasic_SrcType },
+            { SkXfermode::kMultiply_Mode,     "Multiply",     kAll_SrcType   },
+            { SkXfermode::kHue_Mode,          "Hue",          kBasic_SrcType },
+            { SkXfermode::kSaturation_Mode,   "Saturation",   kBasic_SrcType },
+            { SkXfermode::kColor_Mode,        "Color",        kBasic_SrcType },
+            { SkXfermode::kLuminosity_Mode,   "Luminosity",   kBasic_SrcType },
         };
 
         const SkScalar w = SkIntToScalar(W);
@@ -136,9 +209,13 @@
         const int W = 5;
 
         SkScalar x0 = 0;
-        for (int twice = 0; twice < 2; twice++) {
-            SkScalar x = x0, y = 0;
+        SkScalar y0 = 0;
+        for (int sourceType = 1; sourceType & kAll_SrcType; sourceType <<= 1) {
+            SkScalar x = x0, y = y0;
             for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+                if ((gModes[i].fSourceTypeMask & sourceType) == 0) {
+                    continue;
+                }
                 SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
                 SkAutoUnref aur(mode);
                 SkRect r;
@@ -150,7 +227,8 @@
                 canvas->drawRect(r, p);
 
                 canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
-                draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
+                draw_mode(canvas, mode, static_cast<SrcType>(sourceType),
+                          r.fLeft, r.fTop);
                 canvas->restore();
 
                 r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
@@ -168,7 +246,15 @@
                     y += h + SkIntToScalar(30);
                 }
             }
-            x0 += SkIntToScalar(400);
+            if (y < 320) {
+                if (x > x0) {
+                    y += h + SkIntToScalar(30);
+                }
+                y0 = y;
+            } else {
+                x0 += SkIntToScalar(400);
+                y0 = 0;
+            }
         }
         s->unref();
     }
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index a8c6186..53e9fc7 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -247,12 +247,11 @@
     void cleanUp(bool clearFontUsage);
     SkPDFFormXObject* createFormXObjectFromDevice();
 
-    // Clear the passed clip from all existing content entries.
-    void clearClipFromContent(const SkClipStack* clipStack,
-                              const SkRegion& clipRegion);
-    void drawFormXObjectWithClip(SkPDFFormXObject* form,
+    void drawFormXObjectWithMask(int xObjectIndex,
+                                 SkPDFFormXObject* mask,
                                  const SkClipStack* clipStack,
                                  const SkRegion& clipRegion,
+                                 SkXfermode::Mode mode,
                                  bool invertClip);
 
     // If the paint or clip is such that we shouldn't draw anything, this
@@ -266,7 +265,8 @@
                                     bool hasText,
                                     SkPDFFormXObject** dst);
     void finishContentEntry(SkXfermode::Mode xfermode,
-                            SkPDFFormXObject* dst);
+                            SkPDFFormXObject* dst,
+                            SkPath* shape);
     bool isContentEmpty();
 
     void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
@@ -276,6 +276,7 @@
                                             bool hasText,
                                             GraphicStateEntry* entry);
     int addGraphicStateResource(SkPDFGraphicState* gs);
+    int addXObjectResource(SkPDFObject* xObject);
 
     void updateFont(const SkPaint& paint, uint16_t glyphID,
                     ContentEntry* contentEntry);
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 2467d9b..73edef9 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -480,7 +480,8 @@
     bool needRegion = false;
     const SkClipStack::Element* clipEntry;
     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
-        if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInverseFilled()) {
+        if (clipEntry->getOp() != SkRegion::kIntersect_Op ||
+                clipEntry->isInverseFilled()) {
             needRegion = true;
             break;
         }
@@ -619,7 +620,8 @@
                        const SkPaint& paint, bool hasText = false)
         : fDevice(device),
           fContentEntry(NULL),
-          fXfermode(SkXfermode::kSrcOver_Mode) {
+          fXfermode(SkXfermode::kSrcOver_Mode),
+          fDstFormXObject(NULL) {
         init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
     }
     ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
@@ -627,27 +629,67 @@
                        const SkPaint& paint, bool hasText = false)
         : fDevice(device),
           fContentEntry(NULL),
-          fXfermode(SkXfermode::kSrcOver_Mode) {
+          fXfermode(SkXfermode::kSrcOver_Mode),
+          fDstFormXObject(NULL) {
         init(clipStack, clipRegion, matrix, paint, hasText);
     }
 
     ~ScopedContentEntry() {
         if (fContentEntry) {
-            fDevice->finishContentEntry(fXfermode, fDstFormXObject);
+            SkPath* shape = &fShape;
+            if (shape->isEmpty()) {
+                shape = NULL;
+            }
+            fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape);
         }
         SkSafeUnref(fDstFormXObject);
     }
 
     ContentEntry* entry() { return fContentEntry; }
+
+    /* Returns true when we explicitly need the shape of the drawing. */
+    bool needShape() {
+        switch (fXfermode) {
+            case SkXfermode::kClear_Mode:
+            case SkXfermode::kSrc_Mode:
+            case SkXfermode::kSrcIn_Mode:
+            case SkXfermode::kSrcOut_Mode:
+            case SkXfermode::kDstIn_Mode:
+            case SkXfermode::kDstOut_Mode:
+            case SkXfermode::kSrcATop_Mode:
+            case SkXfermode::kDstATop_Mode:
+            case SkXfermode::kModulate_Mode:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /* Returns true unless we only need the shape of the drawing. */
+    bool needSource() {
+        if (fXfermode == SkXfermode::kClear_Mode) {
+            return false;
+        }
+        return true;
+    }
+
+    /* If the shape is different than the alpha component of the content, then
+     * setShape should be called with the shape.  In particular, images and
+     * devices have rectangular shape.
+     */
+    void setShape(const SkPath& shape) {
+        fShape = shape;
+    }
+
 private:
     SkPDFDevice* fDevice;
     ContentEntry* fContentEntry;
     SkXfermode::Mode fXfermode;
     SkPDFFormXObject* fDstFormXObject;
+    SkPath fShape;
 
     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
               const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
-        fDstFormXObject = NULL;
         // Shape has to be flatten before we get here.
         if (matrix.hasPerspective()) {
             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
@@ -1044,7 +1086,8 @@
 
     SkMatrix transform = matrix;
     transform.postConcat(*d.fMatrix);
-    this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL, paint);
+    this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL,
+                             paint);
 }
 
 void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
@@ -1055,7 +1098,8 @@
 
     SkMatrix matrix;
     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
-    this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint);
+    this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL,
+                             paint);
 }
 
 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
@@ -1160,7 +1204,8 @@
         // find all the typefaces needed to resolve this run of text
         bool usesOriginalTypeface = false;
         for (uint16_t x = 0; x < numGlyphs; ++x) {
-            // optimization that checks to see if original typeface can resolve the glyph
+            // optimization that checks to see if original typeface can resolve
+            // the glyph
             if (glyphIDs[x] < origGlyphCount) {
                 usesOriginalTypeface = true;
                 continue;
@@ -1168,9 +1213,11 @@
 
             // find the fallback typeface that supports this glyph
             TypefaceFallbackData data;
-            data.typeface = SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(),
-                                                    paint.getPaintOptionsAndroid(),
-                                                    &data.lowerBounds, &data.upperBounds);
+            data.typeface =
+                    SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(),
+                                            paint.getPaintOptionsAndroid(),
+                                            &data.lowerBounds,
+                                            &data.upperBounds);
             // add the typeface and its data if we don't have it
             if (data.typeface && !visitedTypefaces.contains(data)) {
                 visitedTypefaces.push(data);
@@ -1198,7 +1245,8 @@
 
             int tmpGlyphCount = 0;
             for (uint16_t y = 0; y < numGlyphs; ++y) {
-                if (glyphIDs[y] >= data.lowerBounds && glyphIDs[y] < data.upperBounds) {
+                if (glyphIDs[y] >= data.lowerBounds &&
+                        glyphIDs[y] < data.upperBounds) {
                     tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds;
                     memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]),
                            &(pos[y * scalarsPerPos]),
@@ -1276,8 +1324,8 @@
     NOT_IMPLEMENTED("drawVerticies", true);
 }
 
-void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
-                             const SkPaint& paint) {
+void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device,
+                             int x, int y, const SkPaint& paint) {
     if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
         // If we somehow get a raster device, do what our parent would do.
         SkDevice::drawDevice(d, device, x, y, paint);
@@ -1296,10 +1344,18 @@
     if (!content.entry()) {
         return;
     }
+    if (content.needShape()) {
+        SkPath shape;
+        shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+                                       device->width(), device->height()));
+        content.setShape(shape);
+    }
+    if (!content.needSource()) {
+        return;
+    }
 
-    SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
-    fXObjectResources.push(xobject);  // Transfer reference.
-    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+    SkAutoTUnref<SkPDFFormXObject> xObject(new SkPDFFormXObject(pdfDevice));
+    SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
                                 &content.entry()->fContent);
 
     // Merge glyph sets from the drawn device.
@@ -1537,7 +1593,8 @@
         handleLinkToURL(urlData, r, matrix);
         return p.isNoDrawAnnotation();
     }
-    SkData* linkToName = annotationInfo->find(SkAnnotationKeys::Link_Named_Dest_Key());
+    SkData* linkToName = annotationInfo->find(
+            SkAnnotationKeys::Link_Named_Dest_Key());
     if (linkToName) {
         handleLinkToNamedDest(linkToName, r, matrix);
         return p.isNoDrawAnnotation();
@@ -1552,7 +1609,8 @@
     if (!annotationInfo) {
         return false;
     }
-    SkData* nameData = annotationInfo->find(SkAnnotationKeys::Define_Named_Dest_Key());
+    SkData* nameData = annotationInfo->find(
+            SkAnnotationKeys::Define_Named_Dest_Key());
     if (nameData) {
         for (size_t i = 0; i < count; i++) {
             defineNamedDestination(nameData, points[i], matrix);
@@ -1562,7 +1620,8 @@
     return false;
 }
 
-SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r, const SkMatrix& matrix) {
+SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r,
+                                             const SkMatrix& matrix) {
     SkMatrix transform = matrix;
     transform.postConcat(fInitialTransform);
     SkRect translatedRect;
@@ -1648,7 +1707,8 @@
         pdfDest->appendScalar(dest->point.x());
         pdfDest->appendScalar(dest->point.y());
         pdfDest->appendInt(0);  // Leave zoom unchanged
-        dict->insert(static_cast<const char *>(dest->nameData->data()), pdfDest);
+        dict->insert(static_cast<const char *>(dest->nameData->data()),
+                     pdfDest);
     }
 }
 
@@ -1662,51 +1722,31 @@
     return xobject;
 }
 
-void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
-                                       const SkRegion& clipRegion) {
-    if (clipRegion.isEmpty() || isContentEmpty()) {
-        return;
-    }
-    SkAutoTUnref<SkPDFFormXObject> curContent(createFormXObjectFromDevice());
-
-    // Redraw what we already had, but with the clip as a mask.
-    drawFormXObjectWithClip(curContent, clipStack, clipRegion, true);
-}
-
-void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
+void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
+                                          SkPDFFormXObject* mask,
                                           const SkClipStack* clipStack,
                                           const SkRegion& clipRegion,
+                                          SkXfermode::Mode mode,
                                           bool invertClip) {
     if (clipRegion.isEmpty() && !invertClip) {
         return;
     }
 
-    // Create the mask.
+    SkAutoTUnref<SkPDFGraphicState> sMaskGS(
+        SkPDFGraphicState::GetSMaskGraphicState(
+            mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
+
     SkMatrix identity;
     identity.reset();
-    SkDraw draw;
-    draw.fMatrix = &identity;
-    draw.fClip = &clipRegion;
-    draw.fClipStack = clipStack;
-    SkPaint stockPaint;
-    this->drawPaint(draw, stockPaint);
-    SkAutoTUnref<SkPDFFormXObject> maskFormXObject(createFormXObjectFromDevice());
-    SkAutoTUnref<SkPDFGraphicState> sMaskGS(
-        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip,
-                                                SkPDFGraphicState::kAlpha_SMaskMode));
-
-    // Draw the xobject with the clip as a mask.
-    ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
-                                 identity, stockPaint);
+    SkPaint paint;
+    paint.setXfermodeMode(mode);
+    ScopedContentEntry content(this, clipStack, clipRegion, identity, paint);
     if (!content.entry()) {
         return;
     }
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &content.entry()->fContent);
-    SkPDFUtils::DrawFormXObject(fXObjectResources.count(),
-                                &content.entry()->fContent);
-    fXObjectResources.push(xobject);
-    xobject->ref();
+    SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
 
     sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
@@ -1747,28 +1787,32 @@
         paint.getXfermode()->asMode(&xfermode);
     }
 
-    if (xfermode == SkXfermode::kClear_Mode ||
-            xfermode == SkXfermode::kSrc_Mode) {
-        this->clearClipFromContent(clipStack, clipRegion);
-    } else if (xfermode == SkXfermode::kSrcIn_Mode ||
-               xfermode == SkXfermode::kDstIn_Mode ||
-               xfermode == SkXfermode::kSrcOut_Mode ||
-               xfermode == SkXfermode::kDstOut_Mode) {
-        // For the following modes, we use both source and destination, but
-        // we use one as a smask for the other, so we have to make form xobjects
-        // out of both of them: SrcIn, DstIn, SrcOut, DstOut.
-        if (isContentEmpty()) {
-            return NULL;
-        } else {
+    // For the following modes, we want to handle source and destination
+    // separately, so make an object of what's already there.
+    if (xfermode == SkXfermode::kClear_Mode       ||
+            xfermode == SkXfermode::kSrc_Mode     ||
+            xfermode == SkXfermode::kSrcIn_Mode   ||
+            xfermode == SkXfermode::kDstIn_Mode   ||
+            xfermode == SkXfermode::kSrcOut_Mode  ||
+            xfermode == SkXfermode::kDstOut_Mode  ||
+            xfermode == SkXfermode::kSrcATop_Mode ||
+            xfermode == SkXfermode::kDstATop_Mode ||
+            xfermode == SkXfermode::kModulate_Mode) {
+        if (!isContentEmpty()) {
             *dst = createFormXObjectFromDevice();
+            SkASSERT(isContentEmpty());
+        } else if (xfermode != SkXfermode::kSrc_Mode &&
+                   xfermode != SkXfermode::kSrcOut_Mode) {
+            // Except for Src and SrcOut, if there isn't anything already there,
+            // then we're done.
+            return NULL;
         }
     }
     // TODO(vandebo): Figure out how/if we can handle the following modes:
-    // SrcAtop, DestAtop, Xor, Plus.
+    // Xor, Plus.
 
-    // These xfer modes don't draw source at all.
-    if (xfermode == SkXfermode::kClear_Mode ||
-            xfermode == SkXfermode::kDst_Mode) {
+    // Dst xfer mode doesn't draw source at all.
+    if (xfermode == SkXfermode::kDst_Mode) {
         return NULL;
     }
 
@@ -1806,70 +1850,135 @@
 }
 
 void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
-                                     SkPDFFormXObject* dst) {
-    if (xfermode != SkXfermode::kSrcIn_Mode &&
-            xfermode != SkXfermode::kDstIn_Mode &&
-            xfermode != SkXfermode::kSrcOut_Mode &&
-            xfermode != SkXfermode::kDstOut_Mode) {
+                                     SkPDFFormXObject* dst,
+                                     SkPath* shape) {
+    if (xfermode != SkXfermode::kClear_Mode       &&
+            xfermode != SkXfermode::kSrc_Mode     &&
+            xfermode != SkXfermode::kSrcIn_Mode   &&
+            xfermode != SkXfermode::kDstIn_Mode   &&
+            xfermode != SkXfermode::kSrcOut_Mode  &&
+            xfermode != SkXfermode::kDstOut_Mode  &&
+            xfermode != SkXfermode::kSrcATop_Mode &&
+            xfermode != SkXfermode::kDstATop_Mode &&
+            xfermode != SkXfermode::kModulate_Mode) {
         SkASSERT(!dst);
         return;
     }
+    if (!dst) {
+        SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
+                 xfermode == SkXfermode::kSrcOut_Mode);
+        return;
+    }
 
     ContentEntry* contentEntries = getContentEntries()->get();
     SkASSERT(dst);
     SkASSERT(!contentEntries->fNext.get());
     // We have to make a copy of these here because changing the current
-    // content into a form xobject will destroy them.
+    // content into a form-xobject will destroy them.
     SkClipStack clipStack = contentEntries->fState.fClipStack;
     SkRegion clipRegion = contentEntries->fState.fClipRegion;
 
     SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
-    if (!isContentEmpty()) {
+    if (isContentEmpty()) {
+        SkASSERT(xfermode == SkXfermode::kClear_Mode);
+    } else {
+        SkASSERT(!fContentEntries->fNext.get());
         srcFormXObject.reset(createFormXObjectFromDevice());
     }
 
-    drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
-
-    // We've redrawn dst minus the clip area, if there's no src, we're done.
-    if (!srcFormXObject.get()) {
-        return;
-    }
-
     SkMatrix identity;
     identity.reset();
+
+    // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
+    // without alpha.
+    if (xfermode == SkXfermode::kSrcATop_Mode) {
+        // TODO(vandebo): In order to properly support SrcATop we have to track
+        // the shape of what's been drawn at all times. It's the intersection of
+        // the non-transparent parts of the device and the outlines (shape) of
+        // all images and devices drawn.
+        drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
+                                &clipStack, clipRegion,
+                                SkXfermode::kSrcOver_Mode, true);
+    } else {
+        SkAutoTUnref<SkPDFFormXObject> dstMaskStorage;
+        SkPDFFormXObject* dstMask = srcFormXObject.get();
+        if (shape != NULL) {
+            // Draw shape into a form-xobject.
+            SkDraw d;
+            d.fMatrix = &identity;
+            d.fClip = &clipRegion;
+            d.fClipStack = &clipStack;
+            SkPaint filledPaint;
+            filledPaint.setColor(SK_ColorBLACK);
+            filledPaint.setStyle(SkPaint::kFill_Style);
+            this->drawPath(d, *shape, filledPaint, NULL, true);
+
+            dstMaskStorage.reset(createFormXObjectFromDevice());
+            dstMask = dstMaskStorage.get();
+        }
+        drawFormXObjectWithMask(addXObjectResource(dst), dstMask, &clipStack,
+                                clipRegion, SkXfermode::kSrcOver_Mode, true);
+    }
+
     SkPaint stockPaint;
-    ScopedContentEntry inClipContentEntry(this, &fExistingClipStack,
-                                          fExistingClipRegion, identity,
-                                          stockPaint);
-    if (!inClipContentEntry.entry()) {
+
+    if (xfermode == SkXfermode::kClear_Mode) {
+        return;
+    } else if (xfermode == SkXfermode::kSrc_Mode ||
+            xfermode == SkXfermode::kDstATop_Mode) {
+        ScopedContentEntry content(this, &clipStack, clipRegion, identity,
+                                   stockPaint);
+        if (content.entry()) {
+            SkPDFUtils::DrawFormXObject(
+                    this->addXObjectResource(srcFormXObject.get()),
+                    &content.entry()->fContent);
+        }
+        if (xfermode == SkXfermode::kSrc_Mode) {
+            return;
+        }
+    } else if (xfermode == SkXfermode::kSrcATop_Mode) {
+        ScopedContentEntry content(this, &clipStack, clipRegion, identity,
+                                   stockPaint);
+        if (content.entry()) {
+            SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
+                                        &content.entry()->fContent);
+        }
+    }
+
+    SkASSERT(xfermode == SkXfermode::kSrcIn_Mode   ||
+             xfermode == SkXfermode::kDstIn_Mode   ||
+             xfermode == SkXfermode::kSrcOut_Mode  ||
+             xfermode == SkXfermode::kDstOut_Mode  ||
+             xfermode == SkXfermode::kSrcATop_Mode ||
+             xfermode == SkXfermode::kDstATop_Mode ||
+             xfermode == SkXfermode::kModulate_Mode);
+
+    ScopedContentEntry inShapeContentEntry(this, &fExistingClipStack,
+                                           fExistingClipRegion, identity,
+                                           stockPaint);
+    if (!inShapeContentEntry.entry()) {
         return;
     }
 
-    SkAutoTUnref<SkPDFGraphicState> sMaskGS;
     if (xfermode == SkXfermode::kSrcIn_Mode ||
-            xfermode == SkXfermode::kSrcOut_Mode) {
-        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
-                dst,
-                xfermode == SkXfermode::kSrcOut_Mode,
-                SkPDFGraphicState::kAlpha_SMaskMode));
-        fXObjectResources.push(srcFormXObject.get());
-        srcFormXObject.get()->ref();
+            xfermode == SkXfermode::kSrcOut_Mode ||
+            xfermode == SkXfermode::kSrcATop_Mode) {
+        drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
+                                &clipStack, clipRegion,
+                                SkXfermode::kSrcOver_Mode,
+                                xfermode == SkXfermode::kSrcOut_Mode);
     } else {
-        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
-                srcFormXObject.get(),
-                xfermode == SkXfermode::kDstOut_Mode,
-                SkPDFGraphicState::kAlpha_SMaskMode));
-        // dst already added to fXObjectResources in drawFormXObjectWithClip.
+        SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+        if (xfermode == SkXfermode::kModulate_Mode) {
+            drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
+                                    dst, &clipStack, clipRegion,
+                                    SkXfermode::kSrcOver_Mode, false);
+            mode = SkXfermode::kMultiply_Mode;
+        }
+        drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(),
+                                &clipStack, clipRegion, mode,
+                                xfermode == SkXfermode::kDstOut_Mode);
     }
-    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
-                                  &inClipContentEntry.entry()->fContent);
-
-    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
-                                &inClipContentEntry.entry()->fContent);
-
-    sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
-    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
-                                  &inClipContentEntry.entry()->fContent);
 }
 
 bool SkPDFDevice::isContentEmpty() {
@@ -1981,6 +2090,18 @@
     return result;
 }
 
+int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
+    // Assumes that xobject has been canonicalized (so we can directly compare
+    // pointers).
+    int result = fXObjectResources.find(xObject);
+    if (result < 0) {
+        result = fXObjectResources.count();
+        fXObjectResources.push(xObject);
+        xObject->ref();
+    }
+    return result;
+}
+
 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
                              ContentEntry* contentEntry) {
     SkTypeface* typeface = paint.getTypeface();
@@ -2000,7 +2121,8 @@
 }
 
 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
-    SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface, glyphID));
+    SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface,
+                                                               glyphID));
     int resourceIndex = fFontResources.find(newFont.get());
     if (resourceIndex < 0) {
         resourceIndex = fFontResources.count();
@@ -2105,21 +2227,26 @@
                      SkIntToScalar(subset.height()));
     scaled.postConcat(matrix);
     ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint);
-    if (!content.entry()) {
+    if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) {
+        return;
+    }
+    if (content.needShape()) {
+        SkPath shape;
+        shape.addRect(SkRect::MakeWH(subset.width(), subset.height()));
+        shape.transform(matrix);
+        content.setShape(shape);
+    }
+    if (!content.needSource()) {
         return;
     }
 
-    if (srcRect && !subset.intersect(*srcRect)) {
-        return;
-    }
-
-    SkPDFImage* image = SkPDFImage::CreateImage(*bitmap, subset, fEncoder);
+    SkAutoTUnref<SkPDFImage> image(
+        SkPDFImage::CreateImage(*bitmap, subset, fEncoder));
     if (!image) {
         return;
     }
 
-    fXObjectResources.push(image);  // Transfer reference.
-    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+    SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()),
                                 &content.entry()->fContent);
 }
 
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 43d22f3..9f8edfd 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -14,8 +14,6 @@
 static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
     switch (mode) {
         case SkXfermode::kSrcOver_Mode:    return "Normal";
-        // 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";
@@ -41,11 +39,12 @@
         case SkXfermode::kDstIn_Mode:
         case SkXfermode::kSrcOut_Mode:
         case SkXfermode::kDstOut_Mode:
+        case SkXfermode::kSrcATop_Mode:
+        case SkXfermode::kDstATop_Mode:
+        case SkXfermode::kModulate_Mode:
             return "Normal";
 
         // TODO(vandebo): Figure out if we can support more of these modes.
-        case SkXfermode::kSrcATop_Mode:
-        case SkXfermode::kDstATop_Mode:
         case SkXfermode::kXor_Mode:
         case SkXfermode::kPlus_Mode:
             return NULL;