Make SkDrawable an SkFlattenable

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

Review URL: https://codereview.chromium.org/1911403004
diff --git a/include/core/SkDrawable.h b/include/core/SkDrawable.h
index 2f0a62d..6fec3fc 100644
--- a/include/core/SkDrawable.h
+++ b/include/core/SkDrawable.h
@@ -8,9 +8,10 @@
 #ifndef SkDrawable_DEFINED
 #define SkDrawable_DEFINED
 
-#include "SkRefCnt.h"
+#include "SkFlattenable.h"
 
 class SkCanvas;
+class SkMatrix;
 class SkPicture;
 struct SkRect;
 
@@ -21,7 +22,7 @@
  *  allow for clients of the drawable that may want to cache the results, the drawable must
  *  change its generation ID whenever its internal state changes such that it will draw differently.
  */
-class SkDrawable : public SkRefCnt {
+class SkDrawable : public SkFlattenable {
 public:
     SkDrawable();
 
@@ -58,6 +59,9 @@
      */
     void notifyDrawingChanged();
 
+    SK_DEFINE_FLATTENABLE_TYPE(SkDrawable)
+    Factory getFactory() const override { return nullptr; }
+
 protected:
     virtual SkRect onGetBounds() = 0;
     virtual void onDraw(SkCanvas*) = 0;
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
index 0ba83da..260ea33 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -71,6 +71,7 @@
 public:
     enum Type {
         kSkColorFilter_Type,
+        kSkDrawable_Type,
         kSkDrawLooper_Type,
         kSkImageFilter_Type,
         kSkMaskFilter_Type,
diff --git a/tests/FlattenDrawableTest.cpp b/tests/FlattenDrawableTest.cpp
new file mode 100644
index 0000000..dd06b41
--- /dev/null
+++ b/tests/FlattenDrawableTest.cpp
@@ -0,0 +1,281 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkDrawable.h"
+#include "SkOnce.h"
+#include "SkPictureRecorder.h"
+#include "SkReadBuffer.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkWriteBuffer.h"
+#include "Test.h"
+
+class IntDrawable : public SkDrawable {
+public:
+    IntDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+        : fA(a)
+        , fB(b)
+        , fC(c)
+        , fD(d)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeUInt(fA);
+        buffer.writeUInt(fB);
+        buffer.writeUInt(fC);
+        buffer.writeUInt(fD);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        uint32_t a = buffer.readUInt();
+        uint32_t b = buffer.readUInt();
+        uint32_t c = buffer.readUInt();
+        uint32_t d = buffer.readUInt();
+        return sk_sp<IntDrawable>(new IntDrawable(a, b, c, d));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    uint32_t a() const { return fA; }
+    uint32_t b() const { return fB; }
+    uint32_t c() const { return fC; }
+    uint32_t d() const { return fD; }
+
+    const char* getTypeName() const override { return "IntDrawable"; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    uint32_t fA;
+    uint32_t fB;
+    uint32_t fC;
+    uint32_t fD;
+};
+
+class PaintDrawable : public SkDrawable {
+public:
+    PaintDrawable(const SkPaint& paint)
+        : fPaint(paint)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writePaint(fPaint);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        SkPaint paint;
+        buffer.readPaint(&paint);
+        return sk_sp<PaintDrawable>(new PaintDrawable(paint));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    const SkPaint& paint() const { return fPaint; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    SkPaint fPaint;
+};
+
+class CompoundDrawable : public SkDrawable {
+public:
+    CompoundDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint)
+        : fIntDrawable(new IntDrawable(a, b, c, d))
+        , fPaintDrawable(new PaintDrawable(paint))
+    {}
+
+    CompoundDrawable(IntDrawable* intDrawable, PaintDrawable* paintDrawable)
+        : fIntDrawable(SkRef(intDrawable))
+        , fPaintDrawable(SkRef(paintDrawable))
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeFlattenable(fIntDrawable);
+        buffer.writeFlattenable(fPaintDrawable);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        SkAutoTUnref<SkFlattenable> intDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(intDrawable);
+        SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName()));
+
+        SkAutoTUnref<SkFlattenable> paintDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(paintDrawable);
+        SkASSERT(!strcmp("PaintDrawable", paintDrawable->getTypeName()));
+
+        return sk_sp<CompoundDrawable>(new CompoundDrawable((IntDrawable*) intDrawable.get(),
+                                                            (PaintDrawable*) paintDrawable.get()));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    IntDrawable* intDrawable() const { return fIntDrawable; }
+    PaintDrawable* paintDrawable() const { return fPaintDrawable; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    SkAutoTUnref<IntDrawable>   fIntDrawable;
+    SkAutoTUnref<PaintDrawable> fPaintDrawable;
+};
+
+class RootDrawable : public SkDrawable {
+public:
+    RootDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint,
+                   uint32_t e, uint32_t f, uint32_t g, uint32_t h, SkDrawable* drawable)
+        : fCompoundDrawable(new CompoundDrawable(a, b, c, d, paint))
+        , fIntDrawable(new IntDrawable(e, f, g, h))
+        , fDrawable(SkRef(drawable))
+    {}
+
+    RootDrawable(CompoundDrawable* compoundDrawable, IntDrawable* intDrawable,
+            SkDrawable* drawable)
+        : fCompoundDrawable(SkRef(compoundDrawable))
+        , fIntDrawable(SkRef(intDrawable))
+        , fDrawable(SkRef(drawable))
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeFlattenable(fCompoundDrawable);
+        buffer.writeFlattenable(fIntDrawable);
+        buffer.writeFlattenable(fDrawable);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        SkAutoTUnref<SkFlattenable> compoundDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(compoundDrawable);
+        SkASSERT(!strcmp("CompoundDrawable", compoundDrawable->getTypeName()));
+
+        SkAutoTUnref<SkFlattenable> intDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(intDrawable);
+        SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName()));
+
+        SkAutoTUnref<SkFlattenable> drawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(drawable);
+
+        return sk_sp<RootDrawable>(new RootDrawable((CompoundDrawable*) compoundDrawable.get(),
+                                                    (IntDrawable*) intDrawable.get(),
+                                                    (SkDrawable*) drawable.get()));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    CompoundDrawable* compoundDrawable() const { return fCompoundDrawable; }
+    IntDrawable* intDrawable() const { return fIntDrawable; }
+    SkDrawable* drawable() const { return fDrawable; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    SkAutoTUnref<CompoundDrawable> fCompoundDrawable;
+    SkAutoTUnref<IntDrawable>      fIntDrawable;
+    SkAutoTUnref<SkDrawable>       fDrawable;
+};
+
+static void register_test_drawables() {
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(IntDrawable)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(PaintDrawable)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(CompoundDrawable)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(RootDrawable)
+}
+
+static void register_test_drawables_once() {
+    static SkOnce once;
+    once(register_test_drawables);
+}
+
+DEF_TEST(FlattenDrawable, r) {
+    register_test_drawables_once();
+
+    // Create and serialize the test drawable
+    SkAutoTUnref<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    SkAutoTUnref<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable));
+    SkWriteBuffer writeBuffer;
+    writeBuffer.writeFlattenable(root);
+
+    // 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());
+
+    // Deserialize and verify the drawable
+    SkAutoTUnref<SkDrawable> out((SkDrawable*)
+            readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+    REPORTER_ASSERT(r, out);
+    REPORTER_ASSERT(r, !strcmp("RootDrawable", out->getTypeName()));
+
+    RootDrawable* rootOut = (RootDrawable*) out.get();
+    REPORTER_ASSERT(r, 5 == rootOut->compoundDrawable()->intDrawable()->a());
+    REPORTER_ASSERT(r, 6 == rootOut->compoundDrawable()->intDrawable()->b());
+    REPORTER_ASSERT(r, 7 == rootOut->compoundDrawable()->intDrawable()->c());
+    REPORTER_ASSERT(r, 8 == rootOut->compoundDrawable()->intDrawable()->d());
+    REPORTER_ASSERT(r, SK_ColorBLUE ==
+            rootOut->compoundDrawable()->paintDrawable()->paint().getColor());
+    REPORTER_ASSERT(r, 9 == rootOut->intDrawable()->a());
+    REPORTER_ASSERT(r, 10 == rootOut->intDrawable()->b());
+    REPORTER_ASSERT(r, 11 == rootOut->intDrawable()->c());
+    REPORTER_ASSERT(r, 12 == rootOut->intDrawable()->d());
+
+    // Note that we can still recognize the generic drawable as an IntDrawable
+    SkDrawable* generic = rootOut->drawable();
+    REPORTER_ASSERT(r, !strcmp("IntDrawable", generic->getTypeName()));
+    IntDrawable* integer = (IntDrawable*) generic;
+    REPORTER_ASSERT(r, 1 == integer->a());
+    REPORTER_ASSERT(r, 2 == integer->b());
+    REPORTER_ASSERT(r, 3 == integer->c());
+    REPORTER_ASSERT(r, 4 == integer->d());
+}
+
+static sk_sp<SkFlattenable> custom_create_proc(SkReadBuffer& buffer) {
+    sk_sp<SkFlattenable> drawable = IntDrawable::CreateProc(buffer);
+    IntDrawable* intDrawable = (IntDrawable*) drawable.get();
+    return sk_sp<IntDrawable>(new IntDrawable(intDrawable->a() + 1, intDrawable->b() + 1,
+                                              intDrawable->c() + 1, intDrawable->d() + 1));
+}
+
+DEF_TEST(FlattenCustomDrawable, r) {
+    // No need to register the drawables since we will be using a custom proc.
+
+    // Create and serialize the test drawable
+    SkAutoTUnref<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
+    SkWriteBuffer writeBuffer;
+    writeBuffer.writeFlattenable(drawable);
+
+    // 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 a custom factory with the read buffer
+    readBuffer.setCustomFactory(SkString("IntDrawable"), &custom_create_proc);
+
+    // Deserialize and verify the drawable
+    SkAutoTUnref<IntDrawable> out((IntDrawable*)
+            readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+    REPORTER_ASSERT(r, out);
+    REPORTER_ASSERT(r, 2 == out->a());
+    REPORTER_ASSERT(r, 3 == out->b());
+    REPORTER_ASSERT(r, 4 == out->c());
+    REPORTER_ASSERT(r, 5 == out->d());
+}