[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.

R=edisonn@google.com, reed@google.com

Review URL: https://codereview.appspot.com/4631078

git-svn-id: http://skia.googlecode.com/svn/trunk@12034 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index a7d2bd4..91f8e87 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);
@@ -1054,7 +1096,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,
@@ -1065,7 +1108,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,
@@ -1170,7 +1214,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;
@@ -1178,9 +1223,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);
@@ -1208,7 +1255,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]),
@@ -1286,8 +1334,8 @@
     NOT_IMPLEMENTED("drawVerticies", true);
 }
 
-void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device, int x, int y,
-                             const SkPaint& paint) {
+void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* 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.
         INHERITED::drawDevice(d, device, x, y, paint);
@@ -1306,10 +1354,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.
@@ -1552,7 +1608,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();
@@ -1567,7 +1624,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);
@@ -1577,7 +1635,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;
@@ -1663,7 +1722,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);
     }
 }
 
@@ -1677,51 +1737,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()),
@@ -1762,28 +1802,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;
     }
 
@@ -1821,70 +1865,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() {
@@ -1996,6 +2105,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();
@@ -2015,7 +2136,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();
@@ -2119,21 +2241,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);
 }