[PDF] Add support for SrcIn, SrcOut, DstIn, DstOut xfermodes.
authorvandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 13 May 2011 03:50:38 +0000 (03:50 +0000)
committervandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 13 May 2011 03:50:38 +0000 (03:50 +0000)
This change uses the soft mask (aka soft clip) functionality of PDF to implement the xfermodes.  It has to put existing content (dst) into a form xobject as well as putting the new (src) content into a different form xobject.  It then draws one of them with the other as the soft mask.
To accomplish this, we add a call to finishContentEntry after each call to setUpContentEntry - this is kind of a hack, but I don't see a better way to extract src.
Unfortunately, soft mask is specified in the Graphic State PDF object (and not in the form xobject), so when handling one of these modes, we add a one time GS object to set the soft mask and invoke a simple GS to reset the soft mask when done.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@1320 2bbb7eff-a529-9590-31e7-b0007b416f81

include/pdf/SkPDFDevice.h
include/pdf/SkPDFGraphicState.h
include/pdf/SkPDFUtils.h
src/pdf/SkPDFDevice.cpp
src/pdf/SkPDFGraphicState.cpp
src/pdf/SkPDFUtils.cpp

index 9ab0ae5..cdae413 100644 (file)
@@ -28,6 +28,7 @@ class SkPDFArray;
 class SkPDFDevice;
 class SkPDFDict;
 class SkPDFFont;
+class SkPDFFormXObject;
 class SkPDFGraphicState;
 class SkPDFObject;
 class SkPDFShader;
@@ -155,6 +156,7 @@ private:
 
     SkTScopedPtr<ContentEntry> fContentEntries;
     ContentEntry* fCurrentContentEntry;
+    SkRefPtr<SkPDFFormXObject> fDstFormXObject;
 
     // For use by the DeviceFactory.
     SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
@@ -162,6 +164,7 @@ private:
 
     void init();
     void cleanUp();
+    void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
 
     // If the paint or clip is such that we shouldn't draw anything, these
     // return false and do not create a content entry.
@@ -174,12 +177,14 @@ private:
                                   const SkRegion& clipRegion,
                                   const SkMatrix& matrix,
                                   const SkPaint& paint);
+    void finishContentEntry(const SkPaint& paint);
     void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
                                             const SkClipStack& clipStack,
                                             const SkRegion& clipRegion,
                                             const SkPaint& paint,
                                             bool hasText,
                                             GraphicStateEntry* entry);
+    int addGraphicStateResource(SkPDFGraphicState* gs);
 
     void updateFont(const SkPaint& paint, uint16_t glyphID);
     int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
index 7fb09d4..b43c107 100644 (file)
 #include "SkTemplates.h"
 #include "SkThread.h"
 
+class SkPDFFormXObject;
+
 /** \class SkPDFGraphicState
     SkPaint objects roughly correspond to graphic state dictionaries that can
-    be installed.  So that a given dictionary is only output to the pdf file
-    once, we want to canonicalize them.  Static methods in this class manage
+    be installed. So that a given dictionary is only output to the pdf file
+    once, we want to canonicalize them. Static methods in this class manage
     a weakly referenced set of SkPDFGraphicState objects: when the last
     reference to a SkPDFGraphicState is removed, it removes itself from the
     static set of objects.
@@ -35,6 +37,8 @@ class SkPDFGraphicState : public SkPDFDict {
 public:
     virtual ~SkPDFGraphicState();
 
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
     // Override emitObject and getOutputSize so that we can populate
     // the dictionary on demand.
     virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
@@ -43,16 +47,35 @@ public:
 
     /** Get the graphic state for the passed SkPaint. The reference count of
      *  the object is incremented and it is the caller's responsibility to
-     *  unreference it when done.  This is needed to accommodate the weak
+     *  unreference it when done. This is needed to accommodate the weak
      *  reference pattern used when the returned object is new and has no
      *  other references.
      *  @param paint  The SkPaint to emulate.
      */
     static SkPDFGraphicState* getGraphicStateForPaint(const SkPaint& paint);
 
+    /** Make a graphic state that only sets the passed soft mask. The
+     *  reference count of the object is incremented and it is the caller's
+     *  responsibility to unreference it when done.
+     *  @param sMask  The form xobject to use as a soft mask.
+     *  @param invert Indicates if the alpha of the sMask should be inverted.
+     */
+    static SkPDFGraphicState* getSMaskGraphicState(SkPDFFormXObject* sMask,
+                                                   bool invert);
+
+    /** Get a graphic state that only unsets the soft mask. The reference
+     *  count of the object is incremented and it is the caller's responsibility
+     *  to unreference it when done. This is needed to accommodate the weak
+     *  reference pattern used when the returned object is new and has no
+     *  other references.
+     */
+    static SkPDFGraphicState* getNoSMaskGraphicState();
+
 private:
     const SkPaint fPaint;
+    SkTDArray<SkPDFObject*> fResources;
     bool fPopulated;
+    bool fSMask;
 
     class GSCanonicalEntry {
     public:
@@ -70,6 +93,7 @@ private:
     static SkTDArray<GSCanonicalEntry>& canonicalPaints();
     static SkMutex& canonicalPaintsMutex();
 
+    SkPDFGraphicState();
     explicit SkPDFGraphicState(const SkPaint& paint);
 
     void populateDict();
index 0db4885..04d4c7b 100644 (file)
@@ -54,6 +54,8 @@ public:
     static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
                           SkWStream* content);
     static void StrokePath(SkWStream* content);
+    static void DrawFormXObject(int objectIndex, SkWStream* content);
+    static void ApplyGraphicState(int objectIndex, SkWStream* content);
 };
 
 #endif
index 1a3324a..6734c0e 100644 (file)
@@ -423,9 +423,7 @@ void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
     }
 
     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
-        fContentStream->writeText("/G");
-        fContentStream->writeDecAsText(state.fGraphicStateIndex);
-        fContentStream->writeText(" gs\n");
+        SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
     }
 
@@ -556,6 +554,7 @@ void SkPDFDevice::clear(SkColor color) {
     }
 
     internalDrawPaint(paint);
+    finishContentEntry(paint);
 }
 
 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
@@ -566,6 +565,7 @@ void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
     }
 
     internalDrawPaint(newPaint);
+    finishContentEntry(newPaint);
 }
 
 void SkPDFDevice::internalDrawPaint(const SkPaint& paint) {
@@ -651,6 +651,7 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
         default:
             SkASSERT(false);
     }
+    finishContentEntry(*paint);
 }
 
 void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
@@ -676,6 +677,7 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
     SkPDFUtils::AppendRectangle(r, &fCurrentContentEntry->fContent);
     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
                           &fCurrentContentEntry->fContent);
+    finishContentEntry(paint);
 }
 
 void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
@@ -703,6 +705,7 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
     SkPDFUtils::EmitPath(path, &fCurrentContentEntry->fContent);
     SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(),
                           &fCurrentContentEntry->fContent);
+    finishContentEntry(paint);
 }
 
 void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
@@ -798,6 +801,7 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
             drawRect(d, r, paint);
         }
     }
+    finishContentEntry(textPaint);
 }
 
 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
@@ -849,6 +853,7 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
         fCurrentContentEntry->fContent.writeText(" Tj\n");
     }
     fCurrentContentEntry->fContent.writeText("ET\n");
+    finishContentEntry(textPaint);
 }
 
 void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
@@ -889,10 +894,9 @@ void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
     SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
     fXObjectResources.push(xobject);  // Transfer reference.
-    fCurrentContentEntry->fContent.writeText("/X");
-    fCurrentContentEntry->fContent.writeDecAsText(
-            fXObjectResources.count() - 1);
-    fCurrentContentEntry->fContent.writeText(" Do\n");
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+                                &fCurrentContentEntry->fContent);
+    finishContentEntry(paint);
 }
 
 const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
@@ -1038,6 +1042,14 @@ SkStream* SkPDFDevice::content() const {
     return result;
 }
 
+void SkPDFDevice::createFormXObjectFromDevice(
+        SkRefPtr<SkPDFFormXObject>* xobject) {
+    *xobject = new SkPDFFormXObject(this);
+    (*xobject)->unref();  // SkRefPtr and new both took a reference.
+    cleanUp();  // Reset this device to have no content.
+    init();
+}
+
 bool SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
                                     const SkRegion& clipRegion,
                                     const SkMatrix& matrix,
@@ -1081,9 +1093,19 @@ bool SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
         fCurrentContentEntry = fContentEntries.get();
     }
 
-    // TODO(vandebo) For the following modes, we need to calculate various
-    // operations between the source and destination shape:
-    // SrcIn, DstIn, SrcOut, DstOut, SrcAtop, DestAtop, Xor.
+    // 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 (xfermode == SkXfermode::kSrcIn_Mode ||
+            xfermode == SkXfermode::kDstIn_Mode ||
+            xfermode == SkXfermode::kSrcOut_Mode ||
+            xfermode == SkXfermode::kDstOut_Mode) {
+        SkASSERT(fDstFormXObject.get() == NULL);
+        createFormXObjectFromDevice(&fDstFormXObject);
+    }
+
+    // TODO(vandebo) Figure out how/if we can handle the following modes:
+    // SrcAtop, DestAtop, Xor.
 
     // These xfer modes don't draw source at all.
     if (xfermode == SkXfermode::kClear_Mode ||
@@ -1135,6 +1157,57 @@ bool SkPDFDevice::setUpContentEntryForText(const SkClipStack* clipStack,
     return setUpContentEntry(clipStack, clipRegion, matrix, paint, true);
 }
 
+void SkPDFDevice::finishContentEntry(const SkPaint& paint) {
+    SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+    if (paint.getXfermode()) {
+        paint.getXfermode()->asMode(&xfermode);
+    }
+    if (xfermode != SkXfermode::kSrcIn_Mode &&
+            xfermode != SkXfermode::kDstIn_Mode &&
+            xfermode != SkXfermode::kSrcOut_Mode &&
+            xfermode != SkXfermode::kDstOut_Mode) {
+        SkASSERT(fDstFormXObject.get() == NULL);
+        return;
+    }
+
+    SkRefPtr<SkPDFFormXObject> srcFormXObject;
+    createFormXObjectFromDevice(&srcFormXObject);
+
+    SkMatrix identity;
+    identity.reset();
+    SkPaint stockPaint;
+    setUpContentEntry(&fExistingClipStack, fExistingClipRegion, identity,
+                      stockPaint);
+
+    SkRefPtr<SkPDFGraphicState> sMaskGS;
+    if (xfermode == SkXfermode::kSrcIn_Mode ||
+            xfermode == SkXfermode::kSrcOut_Mode) {
+        sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+                fDstFormXObject.get(), xfermode == SkXfermode::kSrcOut_Mode);
+        fXObjectResources.push(srcFormXObject.get());
+        srcFormXObject->ref();
+    } else {
+        sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+                srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
+        fXObjectResources.push(fDstFormXObject.get());
+        fDstFormXObject->ref();
+    }
+    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+                                  &fCurrentContentEntry->fContent);
+
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+                                &fCurrentContentEntry->fContent);
+
+    sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
+    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+                                  &fCurrentContentEntry->fContent);
+
+    fDstFormXObject = NULL;
+    finishContentEntry(stockPaint);
+}
+
 void SkPDFDevice::populateGraphicStateEntryFromPaint(
         const SkMatrix& matrix,
         const SkClipStack& clipStack,
@@ -1212,14 +1285,7 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
         newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint);
     }
     newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
-    // newGraphicState has been canonicalized so we can directly compare
-    // pointers.
-    int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
-    if (resourceIndex < 0) {
-        resourceIndex = fGraphicStateResources.count();
-        fGraphicStateResources.push(newGraphicState.get());
-        newGraphicState->ref();
-    }
+    int resourceIndex = addGraphicStateResource(newGraphicState.get());
     entry->fGraphicStateIndex = resourceIndex;
 
     if (hasText) {
@@ -1230,6 +1296,18 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
     }
 }
 
+int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
+    // Assumes that gs has been canonicalized (so we can directly compare
+    // pointers).
+    int result = fGraphicStateResources.find(gs);
+    if (result < 0) {
+        result = fGraphicStateResources.count();
+        fGraphicStateResources.push(gs);
+        gs->ref();
+    }
+    return result;
+}
+
 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
     SkTypeface* typeface = paint.getTypeface();
     if (fCurrentContentEntry->fState.fFont == NULL ||
@@ -1299,8 +1377,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
     }
 
     fXObjectResources.push(image);  // Transfer reference.
-    fCurrentContentEntry->fContent.writeText("/X");
-    fCurrentContentEntry->fContent.writeDecAsText(
-            fXObjectResources.count() - 1);
-    fCurrentContentEntry->fContent.writeText(" Do\n");
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+                                &fCurrentContentEntry->fContent);
+    finishContentEntry(paint);
 }
index e5badae..48203f6 100644 (file)
  * limitations under the License.
  */
 
+#include "SkPDFFormXObject.h"
 #include "SkPDFGraphicState.h"
 #include "SkPDFUtils.h"
 #include "SkStream.h"
 #include "SkTypes.h"
 
-namespace {
-
-const char* blendModeFromXfermode(SkXfermode::Mode mode) {
+static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
     switch (mode) {
         case SkXfermode::kSrcOver_Mode:    return "Normal";
         case SkXfermode::kMultiply_Mode:   return "Multiply";
@@ -41,13 +40,13 @@ const char* blendModeFromXfermode(SkXfermode::Mode mode) {
         case SkXfermode::kSrc_Mode:
         case SkXfermode::kDst_Mode:
         case SkXfermode::kDstOver_Mode:
-            return "Normal";
-
-        // TODO(vandebo) Figure out if we can support more of these modes.
         case SkXfermode::kSrcIn_Mode:
         case SkXfermode::kDstIn_Mode:
         case SkXfermode::kSrcOut_Mode:
         case SkXfermode::kDstOut_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:
@@ -57,13 +56,23 @@ const char* blendModeFromXfermode(SkXfermode::Mode mode) {
     return NULL;
 }
 
-}
-
 SkPDFGraphicState::~SkPDFGraphicState() {
     SkAutoMutexAcquire lock(canonicalPaintsMutex());
-    int index = find(fPaint);
-    SkASSERT(index >= 0);
-    canonicalPaints().removeShuffle(index);
+    if (!fSMask) {
+        int index = find(fPaint);
+        SkASSERT(index >= 0);
+        canonicalPaints().removeShuffle(index);
+    }
+    fResources.unrefAll();
+}
+
+void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    resourceList->setReserve(resourceList->count() + fResources.count());
+    for (int i = 0; i < fResources.count(); i++) {
+        resourceList->push(fResources[i]);
+        fResources[i]->ref();
+        fResources[i]->getResources(resourceList);
+    }
 }
 
 void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
@@ -108,14 +117,82 @@ SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
 }
 
 // static
+SkPDFGraphicState* SkPDFGraphicState::getSMaskGraphicState(
+        SkPDFFormXObject* sMask, bool invert) {
+    // The practical chances of using the same mask more than once are unlikely
+    // enough that it's not worth canonicalizing.
+    SkAutoMutexAcquire lock(canonicalPaintsMutex());
+
+    SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
+    sMaskDict->unref();  // SkRefPtr and new both took a reference.
+    sMaskDict->insert("S", new SkPDFName("Alpha"))->unref();
+    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
+
+    SkPDFGraphicState* result = new SkPDFGraphicState;
+    result->fPopulated = true;
+    result->fSMask = true;
+    result->insert("Type", new SkPDFName("ExtGState"))->unref();
+    result->insert("SMask", sMaskDict.get());
+    result->fResources.push(sMask);
+    sMask->ref();
+
+    if (invert) {
+        // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
+        // a type 2 function, so we use a type 4 function.
+        SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
+        domainAndRange->unref();  // SkRefPtr and new both took a reference.
+        domainAndRange->reserve(2);
+        domainAndRange->append(new SkPDFInt(0))->unref();
+        domainAndRange->append(new SkPDFInt(1))->unref();
+
+        static const char psInvert[] = "{1 exch sub}";
+        SkRefPtr<SkMemoryStream> psInvertStream =
+            new SkMemoryStream(&psInvert, strlen(psInvert), true);
+        psInvertStream->unref();  // SkRefPtr and new both took a reference.
+
+        SkRefPtr<SkPDFStream> invertFunc =
+            new SkPDFStream(psInvertStream.get());
+        result->fResources.push(invertFunc.get());  // Pass the ref from new.
+        invertFunc->insert("FunctionType", new SkPDFInt(4))->unref();
+        invertFunc->insert("Domain", domainAndRange.get());
+        invertFunc->insert("Range", domainAndRange.get());
+
+        sMaskDict->insert("TR", new SkPDFObjRef(invertFunc.get()))->unref();
+    }
+
+    return result;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getNoSMaskGraphicState() {
+    SkAutoMutexAcquire lock(canonicalPaintsMutex());
+    static SkPDFGraphicState* noSMaskGS = NULL;
+    if (!noSMaskGS) {
+        noSMaskGS = new SkPDFGraphicState;
+        noSMaskGS->fPopulated = true;
+        noSMaskGS->fSMask = true;
+        noSMaskGS->insert("Type", new SkPDFName("ExtGState"))->unref();
+        noSMaskGS->insert("SMask", new SkPDFName("None"))->unref();
+    }
+    noSMaskGS->ref();
+    return noSMaskGS;
+}
+
+// static
 int SkPDFGraphicState::find(const SkPaint& paint) {
     GSCanonicalEntry search(&paint);
     return canonicalPaints().find(search);
 }
 
+SkPDFGraphicState::SkPDFGraphicState()
+    : fPopulated(false),
+      fSMask(false) {
+}
+
 SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
     : fPaint(paint),
-      fPopulated(false) {
+      fPopulated(false),
+      fSMask(false) {
 }
 
 // populateDict and operator== have to stay in sync with each other.
@@ -154,11 +231,12 @@ void SkPDFGraphicState::populateDict() {
             fPaint.getXfermode()->asMode(&xfermode);
         // If we don't support the mode, just use kSrcOver_Mode.
         if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
-                blendModeFromXfermode(xfermode) == NULL) {
+                blend_mode_from_xfermode(xfermode) == NULL) {
             xfermode = SkXfermode::kSrcOver_Mode;
             NOT_IMPLEMENTED("unsupported xfermode", false);
         }
-        insert("BM", new SkPDFName(blendModeFromXfermode(xfermode)))->unref();
+        insert("BM",
+               new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref();
     }
 }
 
@@ -185,10 +263,10 @@ bool SkPDFGraphicState::GSCanonicalEntry::operator==(
         aXfermode->asMode(&aXfermodeName);
     }
     if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
-            blendModeFromXfermode(aXfermodeName) == NULL) {
+            blend_mode_from_xfermode(aXfermodeName) == NULL) {
         aXfermodeName = SkXfermode::kSrcOver_Mode;
     }
-    const char* aXfermodeString = blendModeFromXfermode(aXfermodeName);
+    const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
     SkASSERT(aXfermodeString != NULL);
 
     SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
@@ -197,10 +275,10 @@ bool SkPDFGraphicState::GSCanonicalEntry::operator==(
         bXfermode->asMode(&bXfermodeName);
     }
     if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
-            blendModeFromXfermode(bXfermodeName) == NULL) {
+            blend_mode_from_xfermode(bXfermodeName) == NULL) {
         bXfermodeName = SkXfermode::kSrcOver_Mode;
     }
-    const char* bXfermodeString = blendModeFromXfermode(bXfermodeName);
+    const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
     SkASSERT(bXfermodeString != NULL);
 
     return strcmp(aXfermodeString, bXfermodeString) == 0;
index 2935f1e..a838427 100644 (file)
@@ -171,3 +171,17 @@ void SkPDFUtils::StrokePath(SkWStream* content) {
     SkPDFUtils::PaintPath(
         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
 }
+
+// static
+void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
+    content->writeText("/X");
+    content->writeDecAsText(objectIndex);
+    content->writeText(" Do\n");
+}
+
+// static
+void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
+    content->writeText("/G");
+    content->writeDecAsText(objectIndex);
+    content->writeText(" gs\n");
+}