Enable flattening of SkRecordedDrawable

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1913843002

Review-Url: https://codereview.chromium.org/1913843002
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 66c9541..7d0d866 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -238,6 +238,7 @@
         '<(skia_src_path)/core/SkRecordOpts.cpp',
         '<(skia_src_path)/core/SkRecordOpts.h',
         '<(skia_src_path)/core/SkRecordPattern.h',
+        '<(skia_src_path)/core/SkRecordedDrawable.cpp',
         '<(skia_src_path)/core/SkRecorder.cpp',
         '<(skia_src_path)/core/SkRect.cpp',
         '<(skia_src_path)/core/SkRefDict.cpp',
diff --git a/src/core/SkGlobalInitialization_core.cpp b/src/core/SkGlobalInitialization_core.cpp
index d7b4a80..e377437 100644
--- a/src/core/SkGlobalInitialization_core.cpp
+++ b/src/core/SkGlobalInitialization_core.cpp
@@ -14,10 +14,11 @@
 #include "SkFlattenable.h"
 #include "SkImageShader.h"
 #include "SkLocalMatrixShader.h"
+#include "SkMatrixImageFilter.h"
 #include "SkOnce.h"
 #include "SkPathEffect.h"
 #include "SkPictureShader.h"
-#include "SkMatrixImageFilter.h"
+#include "SkRecordedDrawable.h"
 #include "SkXfermode.h"
 
 /*
@@ -49,6 +50,9 @@
     // Xfermode
     SkXfermode::InitializeFlattenables();
 
+    // Drawable
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRecordedDrawable)
+
     // Now initialize any optional/additional effects (implemented in src/ports)
     InitEffects();
 };
diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp
index 92aa241..4de1cc3 100644
--- a/src/core/SkPictureData.cpp
+++ b/src/core/SkPictureData.cpp
@@ -66,6 +66,16 @@
         }
     }
 
+    const SkTDArray<SkDrawable* >& drawables = record.getDrawableRefs();
+    fDrawableCount = drawables.count();
+    if (fDrawableCount > 0) {
+        fDrawableRefs = new SkDrawable* [fDrawableCount];
+        for (int i = 0; i < fDrawableCount; i++) {
+            fDrawableRefs[i] = drawables[i];
+            fDrawableRefs[i]->ref();
+        }
+    }
+
     // templatize to consolidate with similar picture logic?
     const SkTDArray<const SkTextBlob*>& blobs = record.getTextBlobRefs();
     fTextBlobCount = blobs.count();
@@ -89,6 +99,8 @@
 void SkPictureData::init() {
     fPictureRefs = nullptr;
     fPictureCount = 0;
+    fDrawableRefs = nullptr;
+    fDrawableCount = 0;
     fTextBlobRefs = nullptr;
     fTextBlobCount = 0;
     fImageRefs = nullptr;
@@ -102,6 +114,14 @@
     }
     delete[] fPictureRefs;
 
+    for (int i = 0; i < fDrawableCount; i++) {
+        fDrawableRefs[i]->unref();
+    }
+    if (fDrawableCount > 0) {
+        SkASSERT(fDrawableRefs);
+        delete[] fDrawableRefs;
+    }
+
     for (int i = 0; i < fTextBlobCount; i++) {
         fTextBlobRefs[i]->unref();
     }
@@ -306,6 +326,13 @@
         }
     }
 
+    if (fDrawableCount > 0) {
+        write_tag_size(buffer, SK_PICT_DRAWABLE_TAG, fDrawableCount);
+        for (int i = 0; i < fDrawableCount; i++) {
+            buffer.writeFlattenable(fDrawableRefs[i]);
+        }
+    }
+
     // Write this picture playback's data into a writebuffer
     this->flattenToBuffer(buffer);
     buffer.write32(SK_PICT_EOF_TAG);
@@ -450,6 +477,10 @@
     return SkPicture::MakeFromBuffer(buffer).release();
 }
 
+static const SkDrawable* create_drawable_from_buffer(SkReadBuffer& buffer) {
+    return (SkDrawable*) buffer.readFlattenable(SkFlattenable::kSkDrawable_Type);
+}
+
 template <typename T>
 bool new_array_from_buffer(SkReadBuffer& buffer, uint32_t inCount,
                            const T*** array, int* outCount, const T* (*factory)(SkReadBuffer&)) {
@@ -540,6 +571,12 @@
                 return false;
             }
             break;
+        case SK_PICT_DRAWABLE_TAG:
+            if (!new_array_from_buffer(buffer, size, (const SkDrawable***)&fDrawableRefs,
+                                       &fDrawableCount, create_drawable_from_buffer)) {
+                return false;
+            }
+            break;
         default:
             // The tag was invalid.
             return false;
diff --git a/src/core/SkPictureData.h b/src/core/SkPictureData.h
index dbb117e..4870305 100644
--- a/src/core/SkPictureData.h
+++ b/src/core/SkPictureData.h
@@ -9,6 +9,7 @@
 #define SkPictureData_DEFINED
 
 #include "SkBitmap.h"
+#include "SkDrawable.h"
 #include "SkPicture.h"
 #include "SkPictureContentInfo.h"
 #include "SkPictureFlat.h"
@@ -43,6 +44,7 @@
 #define SK_PICT_FACTORY_TAG    SkSetFourByteTag('f', 'a', 'c', 't')
 #define SK_PICT_TYPEFACE_TAG   SkSetFourByteTag('t', 'p', 'f', 'c')
 #define SK_PICT_PICTURE_TAG    SkSetFourByteTag('p', 'c', 't', 'r')
+#define SK_PICT_DRAWABLE_TAG   SkSetFourByteTag('d', 'r', 'a', 'w')
 
 // This tag specifies the size of the ReadBuffer, needed for the following tags
 #define SK_PICT_BUFFER_SIZE_TAG     SkSetFourByteTag('a', 'r', 'a', 'y')
@@ -107,6 +109,12 @@
         return reader->validateIndex(index, fPictureCount) ? fPictureRefs[index] : nullptr;
     }
 
+    SkDrawable* getDrawable(SkReadBuffer* reader) const {
+        int index = reader->readInt();
+        SkASSERT(index > 0 && index <= fDrawableCount);
+        return fDrawableRefs[index - 1];
+    }
+
     const SkPaint* getPaint(SkReadBuffer* reader) const {
         const int index = reader->readInt() - 1;
         return reader->validateIndex(index, fPaints.count()) ? &fPaints[index] : nullptr;
@@ -160,6 +168,8 @@
 
     const SkPicture** fPictureRefs;
     int fPictureCount;
+    SkDrawable** fDrawableRefs;
+    int fDrawableCount;
     const SkTextBlob** fTextBlobRefs;
     int fTextBlobCount;
     const SkImage** fImageRefs;
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index cd750bf..efd9af8 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -31,6 +31,8 @@
     DRAW_BITMAP_RECT,
     DRAW_CLEAR,
     DRAW_DATA,
+    DRAW_DRAWABLE,
+    DRAW_DRAWABLE_MATRIX,
     DRAW_OVAL,
     DRAW_PAINT,
     DRAW_PATH,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 3316fe9..beb745f 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -257,6 +257,15 @@
             (void)reader->skip(length);
             // skip handles padding the read out to a multiple of 4
         } break;
+        case DRAW_DRAWABLE:
+            canvas->drawDrawable(fPictureData->getDrawable(reader));
+            break;
+        case DRAW_DRAWABLE_MATRIX: {
+            SkMatrix matrix;
+            reader->readMatrix(&matrix);
+            SkDrawable* drawable = fPictureData->getDrawable(reader);
+            canvas->drawDrawable(drawable, &matrix);
+        } break;
         case DRAW_DRRECT: {
             const SkPaint& paint = *fPictureData->getPaint(reader);
             SkRRect outer, inner;
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index afde01b..4a6ece1 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -33,6 +33,7 @@
 SkPictureRecord::~SkPictureRecord() {
     fImageRefs.unrefAll();
     fPictureRefs.unrefAll();
+    fDrawableRefs.unrefAll();
     fTextBlobRefs.unrefAll();
 }
 
@@ -637,6 +638,23 @@
     this->validate(initialOffset, size);
 }
 
+void SkPictureRecord::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
+    // op + drawable index
+    size_t size = 2 * kUInt32Size;
+    size_t initialOffset;
+
+    if (nullptr == matrix) {
+        initialOffset = this->addDraw(DRAW_DRAWABLE, &size);
+        this->addDrawable(drawable);
+    } else {
+        size += matrix->writeToMemory(nullptr);    // matrix
+        initialOffset = this->addDraw(DRAW_DRAWABLE_MATRIX, &size);
+        this->addMatrix(*matrix);
+        this->addDrawable(drawable);
+    }
+    this->validate(initialOffset, size);
+}
+
 void SkPictureRecord::onDrawVertices(VertexMode vmode, int vertexCount,
                                      const SkPoint vertices[], const SkPoint texs[],
                                      const SkColor colors[], SkXfermode* xfer,
@@ -918,6 +936,17 @@
     this->addInt(index + 1);
 }
 
+void SkPictureRecord::addDrawable(SkDrawable* drawable) {
+    int index = fDrawableRefs.find(drawable);
+    if (index < 0) {    // not found
+        index = fDrawableRefs.count();
+        *fDrawableRefs.append() = drawable;
+        drawable->ref();
+    }
+    // follow the convention of recording a 1-based index
+    this->addInt(index + 1);
+}
+
 void SkPictureRecord::addPoint(const SkPoint& point) {
     fWriter.writePoint(point);
 }
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 129f94f..59bd92d 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -35,6 +35,10 @@
         return fPictureRefs;
     }
 
+    const SkTDArray<SkDrawable* >& getDrawableRefs() const {
+        return fDrawableRefs;
+    }
+
     const SkTDArray<const SkTextBlob* >& getTextBlobRefs() const {
         return fTextBlobRefs;
     }
@@ -131,6 +135,7 @@
     void addPatch(const SkPoint cubics[12]);
     void addPath(const SkPath& path);
     void addPicture(const SkPicture* picture);
+    void addDrawable(SkDrawable* picture);
     void addPoint(const SkPoint& point);
     void addPoints(const SkPoint pts[], int count);
     void addRect(const SkRect& rect);
@@ -204,6 +209,7 @@
     void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
+    void onDrawDrawable(SkDrawable*, const SkMatrix*) override;
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
     int addPathToHeap(const SkPath& path);  // does not write to ops stream
@@ -238,6 +244,7 @@
     // we ref each item in these arrays
     SkTDArray<const SkImage*>    fImageRefs;
     SkTDArray<const SkPicture*>  fPictureRefs;
+    SkTDArray<SkDrawable*>       fDrawableRefs;
     SkTDArray<const SkTextBlob*> fTextBlobRefs;
 
     uint32_t fRecordFlags;
diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp
index b6bb34d..a157d0d 100644
--- a/src/core/SkPictureRecorder.cpp
+++ b/src/core/SkPictureRecorder.cpp
@@ -14,6 +14,7 @@
 #include "SkRecord.h"
 #include "SkRecordDraw.h"
 #include "SkRecordOpts.h"
+#include "SkRecordedDrawable.h"
 #include "SkRecorder.h"
 #include "SkTypes.h"
 
@@ -117,66 +118,6 @@
     SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, nullptr/*bbh*/, nullptr/*callback*/);
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkRecordedDrawable : public SkDrawable {
-    SkAutoTUnref<SkRecord>          fRecord;
-    SkAutoTUnref<SkBBoxHierarchy>   fBBH;
-    SkAutoTDelete<SkDrawableList>   fDrawableList;
-    const SkRect                    fBounds;
-    const bool                      fDoSaveLayerInfo;
-
-public:
-    SkRecordedDrawable(SkRecord* record, SkBBoxHierarchy* bbh, SkDrawableList* drawableList,
-                       const SkRect& bounds, bool doSaveLayerInfo)
-        : fRecord(SkRef(record))
-        , fBBH(SkSafeRef(bbh))
-        , fDrawableList(drawableList)   // we take ownership
-        , fBounds(bounds)
-        , fDoSaveLayerInfo(doSaveLayerInfo)
-    {}
-
-protected:
-    SkRect onGetBounds() override { return fBounds; }
-
-    void onDraw(SkCanvas* canvas) override {
-        SkDrawable* const* drawables = nullptr;
-        int drawableCount = 0;
-        if (fDrawableList) {
-            drawables = fDrawableList->begin();
-            drawableCount = fDrawableList->count();
-        }
-        SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, fBBH, nullptr/*callback*/);
-    }
-
-    SkPicture* onNewPictureSnapshot() override {
-        SkBigPicture::SnapshotArray* pictList = nullptr;
-        if (fDrawableList) {
-            // TODO: should we plumb-down the BBHFactory and recordFlags from our host
-            //       PictureRecorder?
-            pictList = fDrawableList->newDrawableSnapshot();
-        }
-
-        SkAutoTUnref<SkLayerInfo> saveLayerData;
-        if (fBBH && fDoSaveLayerInfo) {
-            // TODO: can we avoid work by not allocating / filling these bounds?
-            SkAutoTMalloc<SkRect> scratchBounds(fRecord->count());
-            saveLayerData.reset(new SkLayerInfo);
-
-            SkRecordComputeLayers(fBounds, *fRecord, scratchBounds, pictList, saveLayerData);
-        }
-
-        size_t subPictureBytes = 0;
-        for (int i = 0; pictList && i < pictList->count(); i++) {
-            subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
-        }
-        // SkBigPicture will take ownership of a ref on both fRecord and fBBH.
-        // We're not willing to give up our ownership, so we must ref them for SkPicture.
-        return new SkBigPicture(fBounds, SkRef(fRecord.get()), pictList, SkSafeRef(fBBH.get()),
-                                saveLayerData.release(), subPictureBytes);
-    }
-};
-
 sk_sp<SkDrawable> SkPictureRecorder::finishRecordingAsDrawable() {
     fActivelyRecording = false;
     fRecorder->flushMiniRecorder();
diff --git a/src/core/SkRecordedDrawable.cpp b/src/core/SkRecordedDrawable.cpp
new file mode 100644
index 0000000..9b5874f
--- /dev/null
+++ b/src/core/SkRecordedDrawable.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkLayerInfo.h"
+#include "SkMatrix.h"
+#include "SkPictureData.h"
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+#include "SkPictureRecorder.h"
+#include "SkPictureUtils.h"
+#include "SkRecordedDrawable.h"
+#include "SkRecordDraw.h"
+
+void SkRecordedDrawable::onDraw(SkCanvas* canvas) {
+    SkDrawable* const* drawables = nullptr;
+    int drawableCount = 0;
+    if (fDrawableList) {
+        drawables = fDrawableList->begin();
+        drawableCount = fDrawableList->count();
+    }
+    SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, fBBH, nullptr/*callback*/);
+}
+
+SkPicture* SkRecordedDrawable::onNewPictureSnapshot() {
+    SkBigPicture::SnapshotArray* pictList = nullptr;
+    if (fDrawableList) {
+        // TODO: should we plumb-down the BBHFactory and recordFlags from our host
+        //       PictureRecorder?
+        pictList = fDrawableList->newDrawableSnapshot();
+    }
+
+    SkAutoTUnref<SkLayerInfo> saveLayerData;
+    if (fBBH && fDoSaveLayerInfo) {
+        // TODO: can we avoid work by not allocating / filling these bounds?
+        SkAutoTMalloc<SkRect> scratchBounds(fRecord->count());
+        saveLayerData.reset(new SkLayerInfo);
+
+        SkRecordComputeLayers(fBounds, *fRecord, scratchBounds, pictList, saveLayerData);
+    }
+
+    size_t subPictureBytes = 0;
+    for (int i = 0; pictList && i < pictList->count(); i++) {
+        subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
+    }
+    // SkBigPicture will take ownership of a ref on both fRecord and fBBH.
+    // We're not willing to give up our ownership, so we must ref them for SkPicture.
+    return new SkBigPicture(fBounds, SkRef(fRecord.get()), pictList, SkSafeRef(fBBH.get()),
+                            saveLayerData.release(), subPictureBytes);
+}
+
+void SkRecordedDrawable::flatten(SkWriteBuffer& buffer) const {
+    // Write the bounds.
+    buffer.writeRect(fBounds);
+
+    // Create an SkPictureRecord to record the draw commands.
+    SkPictInfo info;
+    SkPictureRecord pictureRecord(SkISize::Make(fBounds.width(), fBounds.height()), 0);
+
+    // If the query contains the whole picture, don't bother with the bounding box hierarchy.
+    SkRect clipBounds;
+    pictureRecord.getClipBounds(&clipBounds);
+    SkBBoxHierarchy* bbh;
+    if (clipBounds.contains(fBounds)) {
+        bbh = nullptr;
+    } else {
+        bbh = fBBH.get();
+    }
+
+    // Record the draw commands.
+    pictureRecord.beginRecording();
+    SkRecordDraw(*fRecord, &pictureRecord, nullptr, fDrawableList->begin(), fDrawableList->count(),
+                bbh, nullptr);
+    pictureRecord.endRecording();
+
+    // Flatten the recorded commands and drawables.
+    SkPictureData pictureData(pictureRecord, info, false);
+    pictureData.flatten(buffer);
+}
+
+sk_sp<SkFlattenable> SkRecordedDrawable::CreateProc(SkReadBuffer& buffer) {
+    // Read the bounds.
+    SkRect bounds;
+    buffer.readRect(&bounds);
+
+    // Unflatten into a SkPictureData.
+    SkPictInfo info;
+    info.fCullRect = bounds;
+    SkAutoTDelete<SkPictureData> pictureData(SkPictureData::CreateFromBuffer(buffer, info));
+    if (!pictureData) {
+        return nullptr;
+    }
+
+    // Create a drawable.
+    SkPicturePlayback playback(pictureData);
+    SkPictureRecorder recorder;
+    playback.draw(recorder.beginRecording(bounds), nullptr, &buffer);
+    return recorder.finishRecordingAsDrawable();
+}
diff --git a/src/core/SkRecordedDrawable.h b/src/core/SkRecordedDrawable.h
new file mode 100644
index 0000000..e9973b4
--- /dev/null
+++ b/src/core/SkRecordedDrawable.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBBoxHierarchy.h"
+#include "SkDrawable.h"
+#include "SkRecord.h"
+#include "SkRecorder.h"
+
+class SkRecordedDrawable : public SkDrawable {
+public:
+    SkRecordedDrawable(SkRecord* record, SkBBoxHierarchy* bbh, SkDrawableList* drawableList,
+                       const SkRect& bounds, bool doSaveLayerInfo)
+        : fRecord(SkRef(record))
+        , fBBH(SkSafeRef(bbh))
+        , fDrawableList(drawableList)   // we take ownership
+        , fBounds(bounds)
+        , fDoSaveLayerInfo(doSaveLayerInfo)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override;
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer);
+
+    Factory getFactory() const override { return CreateProc; }
+
+protected:
+    SkRect onGetBounds() override { return fBounds; }
+
+    void onDraw(SkCanvas* canvas) override;
+
+    SkPicture* onNewPictureSnapshot() override;
+
+private:
+    SkAutoTUnref<SkRecord>          fRecord;
+    SkAutoTUnref<SkBBoxHierarchy>   fBBH;
+    SkAutoTDelete<SkDrawableList>   fDrawableList;
+    const SkRect                    fBounds;
+    const bool                      fDoSaveLayerInfo;
+};
diff --git a/tests/FlattenDrawableTest.cpp b/tests/FlattenDrawableTest.cpp
index c7fff12..2132729 100644
--- a/tests/FlattenDrawableTest.cpp
+++ b/tests/FlattenDrawableTest.cpp
@@ -246,3 +246,42 @@
     REPORTER_ASSERT(r, 3 == integer->c());
     REPORTER_ASSERT(r, 4 == integer->d());
 }
+
+DEF_TEST(FlattenRecordedDrawable, r) {
+    // Record a set of canvas draw commands
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(1000.0f, 1000.0f);
+    canvas->drawPoint(42.0f, 17.0f, SK_ColorGREEN);
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    canvas->drawPaint(paint);
+    SkPaint textPaint;
+    textPaint.setColor(SK_ColorBLUE);
+    canvas->drawText("TEXT", 4, 467.0f, 100.0f, textPaint);
+
+    // Draw some drawables as well
+    SkAutoTUnref<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
+    SkAutoTUnref<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable));
+    canvas->drawDrawable(root, 747.0f, 242.0f);
+    SkAutoTUnref<PaintDrawable> paintDrawable(new PaintDrawable(paint));
+    canvas->drawDrawable(paintDrawable, 500.0, 500.0f);
+    SkAutoTUnref<CompoundDrawable> comDrawable(new CompoundDrawable(13, 14, 15, 16, textPaint));
+    canvas->drawDrawable(comDrawable, 10.0f, 10.0f);
+
+    // Serialize the recorded drawable
+    sk_sp<SkDrawable> recordedDrawable = recorder.finishRecordingAsDrawable();
+    SkWriteBuffer writeBuffer;
+    writeBuffer.writeFlattenable(recordedDrawable.get());
+
+    // Copy the contents of the write buffer into a read buffer
+    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
+    writeBuffer.writeToMemory(data->writable_data());
+    SkReadBuffer readBuffer(data->data(), data->size());
+    register_test_drawables(readBuffer);
+
+    // Deserialize and verify the drawable
+    SkAutoTUnref<SkDrawable> out((SkDrawable*)
+            readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+    REPORTER_ASSERT(r, out);
+    REPORTER_ASSERT(r, !strcmp("SkRecordedDrawable", out->getTypeName()));
+}