SkPdf: SkPDFFormXObject de-class-ified.

We don't need an object, just a few standard fields on the
base class; the change lets us get rid of a bunch of
boilerplate code.

I think this also reduces the cognitive load of the SkPDF
internals.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2185803003

Review-Url: https://codereview.chromium.org/2185803003
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 4181def..6972f8b 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -627,7 +627,7 @@
     SkPDFDevice* fDevice;
     SkPDFDevice::ContentEntry* fContentEntry;
     SkXfermode::Mode fXfermode;
-    SkPDFFormXObject* fDstFormXObject;
+    SkPDFObject* fDstFormXObject;
     SkPath fShape;
 
     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
@@ -835,7 +835,7 @@
 }
 
 static sk_sp<SkPDFDict> create_link_to_url(const SkData* urlData, const SkRect& r) {
-    auto annotation = create_link_annotation(r);
+    sk_sp<SkPDFDict> annotation = create_link_annotation(r);
     SkString url(static_cast<const char *>(urlData->data()),
                  urlData->size() - 1);
     auto action = sk_make_sp<SkPDFDict>("Action");
@@ -847,7 +847,7 @@
 
 static sk_sp<SkPDFDict> create_link_named_dest(const SkData* nameData,
                                                const SkRect& r) {
-    auto  annotation = create_link_annotation(r);
+    sk_sp<SkPDFDict> annotation = create_link_annotation(r);
     SkString name(static_cast<const char *>(nameData->data()),
                   nameData->size() - 1);
     annotation->insertName("Dest", name);
@@ -1394,7 +1394,7 @@
         return;
     }
 
-    auto xObject = sk_make_sp<SkPDFFormXObject>(pdfDevice);
+    sk_sp<SkPDFObject> xObject = pdfDevice->makeFormXObjectFromDevice();
     SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
                                 &content.entry()->fContent);
 }
@@ -1610,18 +1610,20 @@
     }
 }
 
-SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
-    SkPDFFormXObject* xobject = new SkPDFFormXObject(this);
+sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice() {
+    sk_sp<SkPDFObject> xobject =
+        SkPDFMakeFormXObject(this->content(), this->copyMediaBox(),
+                             this->makeResourceDict(), nullptr);
     // We always draw the form xobjects that we create back into the device, so
     // we simply preserve the font usage instead of pulling it out and merging
     // it back in later.
-    cleanUp();  // Reset this device to have no content.
-    init();
+    this->cleanUp();  // Reset this device to have no content.
+    this->init();
     return xobject;
 }
 
 void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
-                                          SkPDFFormXObject* mask,
+                                          SkPDFObject* mask,
                                           const SkClipStack* clipStack,
                                           const SkRegion& clipRegion,
                                           SkXfermode::Mode mode,
@@ -1630,7 +1632,7 @@
         return;
     }
 
-    auto sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
+    sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
             mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode, fDocument->canon());
 
     SkMatrix identity;
@@ -1658,7 +1660,7 @@
                                              const SkMatrix& matrix,
                                              const SkPaint& paint,
                                              bool hasText,
-                                             SkPDFFormXObject** dst) {
+                                             SkPDFObject** dst) {
     *dst = nullptr;
     if (clipRegion.isEmpty()) {
         return nullptr;
@@ -1699,7 +1701,8 @@
             xfermode == SkXfermode::kDstATop_Mode ||
             xfermode == SkXfermode::kModulate_Mode) {
         if (!isContentEmpty()) {
-            *dst = createFormXObjectFromDevice();
+            // TODO(halcanary): make this safer.
+            *dst = this->makeFormXObjectFromDevice().release();
             SkASSERT(isContentEmpty());
         } else if (xfermode != SkXfermode::kSrc_Mode &&
                    xfermode != SkXfermode::kSrcOut_Mode) {
@@ -1730,7 +1733,7 @@
 }
 
 void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
-                                     SkPDFFormXObject* dst,
+                                     SkPDFObject* dst,
                                      SkPath* shape) {
     if (xfermode != SkXfermode::kClear_Mode       &&
             xfermode != SkXfermode::kSrc_Mode     &&
@@ -1775,7 +1778,7 @@
     identity.reset();
     SkPaint stockPaint;
 
-    sk_sp<SkPDFFormXObject> srcFormXObject;
+    sk_sp<SkPDFObject> srcFormXObject;
     if (isContentEmpty()) {
         // If nothing was drawn and there's no shape, then the draw was a
         // no-op, but dst needs to be restored for that to be true.
@@ -1795,7 +1798,7 @@
         }
     } else {
         SkASSERT(fContentEntries.count() == 1);
-        srcFormXObject.reset(createFormXObjectFromDevice());
+        srcFormXObject = this->makeFormXObjectFromDevice();
     }
 
     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
@@ -1809,8 +1812,8 @@
                                 &fExistingClipStack, fExistingClipRegion,
                                 SkXfermode::kSrcOver_Mode, true);
     } else {
-        sk_sp<SkPDFFormXObject> dstMaskStorage;
-        SkPDFFormXObject* dstMask = srcFormXObject.get();
+        sk_sp<SkPDFObject> dstMaskStorage;
+        SkPDFObject* dstMask = srcFormXObject.get();
         if (shape != nullptr) {
             // Draw shape into a form-xobject.
             SkRasterClip rc(clipRegion);
@@ -1823,7 +1826,7 @@
             filledPaint.setStyle(SkPaint::kFill_Style);
             this->drawPath(d, *shape, filledPaint, nullptr, true);
 
-            dstMaskStorage.reset(createFormXObjectFromDevice());
+            dstMaskStorage = this->makeFormXObjectFromDevice();
             dstMask = dstMaskStorage.get();
         }
         drawFormXObjectWithMask(addXObjectResource(dst), dstMask,
@@ -2184,7 +2187,7 @@
     SkBitmapKey key = imageBitmap.getKey();
     sk_sp<SkPDFObject> pdfimage = fDocument->canon()->findPDFBitmap(key);
     if (!pdfimage) {
-        auto img = imageBitmap.makeImage();
+        sk_sp<SkImage> img = imageBitmap.makeImage();
         if (!img) {
             return;
         }
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 6a167d9..d5d52f9 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -29,8 +29,8 @@
 class SkPDFDocument;
 class SkPDFDict;
 class SkPDFFont;
-class SkPDFFormXObject;
 class SkPDFObject;
+class SkPDFStream;
 class SkRRect;
 
 /** \class SkPDFDevice
@@ -268,10 +268,10 @@
 
     void init();
     void cleanUp();
-    SkPDFFormXObject* createFormXObjectFromDevice();
+    sk_sp<SkPDFObject> makeFormXObjectFromDevice();
 
     void drawFormXObjectWithMask(int xObjectIndex,
-                                 SkPDFFormXObject* mask,
+                                 SkPDFObject* mask,
                                  const SkClipStack* clipStack,
                                  const SkRegion& clipRegion,
                                  SkXfermode::Mode mode,
@@ -286,9 +286,9 @@
                                     const SkMatrix& matrix,
                                     const SkPaint& paint,
                                     bool hasText,
-                                    SkPDFFormXObject** dst);
+                                    SkPDFObject** dst);
     void finishContentEntry(SkXfermode::Mode xfermode,
-                            SkPDFFormXObject* dst,
+                            SkPDFObject* dst,
                             SkPath* shape);
     bool isContentEmpty();
 
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index ba49df8..1ab391c 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -8,69 +8,26 @@
 
 #include "SkPDFFormXObject.h"
 
-#include "SkMatrix.h"
-#include "SkPDFDevice.h"
-#include "SkPDFUtils.h"
-#include "SkStream.h"
-#include "SkTypes.h"
+sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
+                                        sk_sp<SkPDFArray> mediaBox,
+                                        sk_sp<SkPDFDict> resourceDict,
+                                        const char* colorSpace) {
+    auto form = sk_make_sp<SkPDFStream>(std::move(content));
+    form->insertName("Type", "XObject");
+    form->insertName("Subtype", "Form");
+    form->insertObject("Resources", std::move(resourceDict));
+    form->insertObject("BBox", std::move(mediaBox));
 
-SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) {
-    // We don't want to keep around device because we'd have two copies
-    // of content, so reference or copy everything we need (content and
-    // resources).
-    auto resourceDict = device->makeResourceDict();
-
-    this->setData(device->content());
-
-    sk_sp<SkPDFArray> bboxArray(device->copyMediaBox());
-    this->init(nullptr, resourceDict.get(), bboxArray.get());
-
-    // We invert the initial transform and apply that to the xobject so that
-    // it doesn't get applied twice. We can't just undo it because it's
-    // embedded in things like shaders and images.
-    if (!device->initialTransform().isIdentity()) {
-        SkMatrix inverse;
-        if (!device->initialTransform().invert(&inverse)) {
-            // The initial transform should be invertible.
-            SkASSERT(false);
-            inverse.reset();
-        }
-        this->insertObject("Matrix", SkPDFUtils::MatrixToArray(inverse));
-    }
-}
-
-/**
- * Creates a FormXObject from a content stream and associated resources.
- */
-SkPDFFormXObject::SkPDFFormXObject(std::unique_ptr<SkStreamAsset> content,
-                                   SkRect bbox,
-                                   SkPDFDict* resourceDict) {
-    this->setData(std::move(content));
-    sk_sp<SkPDFArray> bboxArray(SkPDFUtils::RectToArray(bbox));
-    this->init("DeviceRGB", resourceDict, bboxArray.get());
-}
-
-/**
- * Common initialization code.
- * Note that bbox is unreferenced here, so calling code does not need worry.
- */
-void SkPDFFormXObject::init(const char* colorSpace,
-                            SkPDFDict* resourceDict, SkPDFArray* bbox) {
-    this->insertName("Type", "XObject");
-    this->insertName("Subtype", "Form");
-    this->insertObject("Resources", sk_ref_sp(resourceDict));
-    this->insertObject("BBox", sk_ref_sp(bbox));
-
-    // Right now SkPDFFormXObject is only used for saveLayer, which implies
+    // Right now FormXObject is only used for saveLayer, which implies
     // isolated blending.  Do this conditionally if that changes.
+    // TODO(halcanary): Is this comment obsolete, since we use it for
+    // alpha masks?
     auto group = sk_make_sp<SkPDFDict>("Group");
     group->insertName("S", "Transparency");
-
     if (colorSpace != nullptr) {
         group->insertName("CS", colorSpace);
     }
     group->insertBool("I", true);  // Isolated.
-    this->insertObject("Group", std::move(group));
+    form->insertObject("Group", std::move(group));
+    return form;
 }
-
-SkPDFFormXObject::~SkPDFFormXObject() {}
diff --git a/src/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
index a483a60..6ce8b87 100644
--- a/src/pdf/SkPDFFormXObject.h
+++ b/src/pdf/SkPDFFormXObject.h
@@ -10,41 +10,15 @@
 #define SkPDFFormXObject_DEFINED
 
 #include "SkPDFStream.h"
+#include "SkPDFDevice.h"
 
-class SkPDFArray;
-class SkPDFDevice;
-class SkPDFDict;
-struct SkRect;
-
-/** \class SkPDFFormXObject
-
-    A form XObject; a self contained description of graphics objects.  A form
-    XObject is basically a page object with slightly different syntax, that
-    can be drawn onto a page.
+/** A form XObject is a self contained description of a graphics
+    object.  A form XObject is a page object with slightly different
+    syntax, that can be drawn into a page content stream, just like a
+    bitmap XObject can be drawn into a page content stream.
 */
-
-// The caller could keep track of the form XObjects it creates and
-// canonicalize them, but the Skia API doesn't provide enough context to
-// automatically do it (trivially).
-class SkPDFFormXObject final : public SkPDFStream {
-public:
-    /** Create a PDF form XObject. Entries for the dictionary entries are
-     *  automatically added.
-     *  @param device      The set of graphical elements on this form.
-     */
-    explicit SkPDFFormXObject(SkPDFDevice* device);
-    /**
-     * Create a PDF form XObject from a raw content stream and associated
-     * resources.
-     */
-    explicit SkPDFFormXObject(std::unique_ptr<SkStreamAsset> content,
-                              SkRect bbox,
-                              SkPDFDict* resourceDict);
-    virtual ~SkPDFFormXObject();
-
-private:
-    void init(const char* colorSpace,
-              SkPDFDict* resourceDict, SkPDFArray* bbox);
-};
-
+sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
+                                        sk_sp<SkPDFArray> mediaBox,
+                                        sk_sp<SkPDFDict> resourceDict,
+                                        const char* colorSpace);
 #endif
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 17129bb..3edf745 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -144,7 +144,7 @@
 }
 
 sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
-        SkPDFFormXObject* sMask,
+        SkPDFObject* sMask,
         bool invert,
         SkPDFSMaskMode sMaskMode,
         SkPDFCanon* canon) {
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 569f9da..70b63d4 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -14,7 +14,6 @@
 
 class SkPaint;
 class SkPDFCanon;
-class SkPDFFormXObject;
 
 /** \class SkPDFGraphicState
     SkPaint objects roughly correspond to graphic state dictionaries that can
@@ -52,7 +51,7 @@
      *
      *  These are not de-duped.
      */
-    static sk_sp<SkPDFDict> GetSMaskGraphicState(SkPDFFormXObject* sMask,
+    static sk_sp<SkPDFDict> GetSMaskGraphicState(SkPDFObject* sMask,
                                                  bool invert,
                                                  SkPDFSMaskMode sMaskMode,
                                                  SkPDFCanon* canon);
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index 13b4479..51d7442 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -703,9 +703,10 @@
     auto resources =
         get_gradient_resource_dict(luminosityShader.get(), nullptr);
 
-    sk_sp<SkPDFFormXObject> alphaMask(
-            new SkPDFFormXObject(std::move(alphaStream), bbox, resources.get()));
-
+    auto alphaMask = SkPDFMakeFormXObject(std::move(alphaStream),
+                                          SkPDFUtils::RectToArray(bbox),
+                                          std::move(resources),
+                                          "DeviceRGB");
     return SkPDFGraphicState::GetSMaskGraphicState(
             alphaMask.get(), false,
             SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());