DM: add --serialize

Plus:
  - minor ReplayTask refactoring to share code with SerializeTask
  - move --replay to ReplayTask and --serialize to SerializeTask like WriteTask
  - when --writePath is given, write failures for Replay and Serialize tasks
  - function names have fewer blatant Skia style violations

BUG=
R=bsalomon@google.com

Author: mtklein@google.com

Review URL: https://codereview.chromium.org/32613003

git-svn-id: http://skia.googlecode.com/svn/trunk@11890 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/dm/DMComparisonTask.cpp b/dm/DMComparisonTask.cpp
index 24976f0..bb4e656 100644
--- a/dm/DMComparisonTask.cpp
+++ b/dm/DMComparisonTask.cpp
@@ -13,7 +13,7 @@
     {}
 
 void ComparisonTask::draw() {
-    if (!meetsExpectations(fExpectations, fBitmap)) {
+    if (!MeetsExpectations(fExpectations, fBitmap)) {
         this->fail();
     }
 }
diff --git a/dm/DMCpuTask.cpp b/dm/DMCpuTask.cpp
index 316f341..f6edf23 100644
--- a/dm/DMCpuTask.cpp
+++ b/dm/DMCpuTask.cpp
@@ -1,11 +1,8 @@
 #include "DMCpuTask.h"
 #include "DMReplayTask.h"
+#include "DMSerializeTask.h"
 #include "DMUtil.h"
 #include "DMWriteTask.h"
-#include "SkCommandLineFlags.h"
-
-DEFINE_bool(replay, false, "If true, run replay tests for each CpuTask.");
-// TODO(mtklein): add the other various options
 
 namespace DM {
 
@@ -18,30 +15,26 @@
     : Task(reporter, taskRunner)
     , fGMFactory(gmFactory)
     , fGM(fGMFactory(NULL))
-    , fName(underJoin(fGM->shortName(), name))
-    , fExpectations(expectations.get(png(fName).c_str()))
+    , fName(UnderJoin(fGM->shortName(), name))
+    , fExpectations(expectations.get(Png(fName).c_str()))
     , fConfig(config)
     {}
 
 void CpuTask::draw() {
     SkBitmap bitmap;
-    bitmap.setConfig(fConfig, SkScalarCeilToInt(fGM->width()), SkScalarCeilToInt(fGM->height()));
-    bitmap.allocPixels();
-    bitmap.eraseColor(0x00000000);
-    SkCanvas canvas(bitmap);
+    SetupBitmap(fConfig, fGM.get(), &bitmap);
 
+    SkCanvas canvas(bitmap);
     canvas.concat(fGM->getInitialTransform());
     fGM->draw(&canvas);
     canvas.flush();
 
-    if (!meetsExpectations(fExpectations, bitmap)) {
+    if (!MeetsExpectations(fExpectations, bitmap)) {
         this->fail();
     }
 
-    if (FLAGS_replay) {
-        this->spawnChild(SkNEW_ARGS(ReplayTask,
-                                   ("replay", *this, fGMFactory(NULL), bitmap)));
-    }
+    this->spawnChild(SkNEW_ARGS(ReplayTask, (*this, fGMFactory(NULL), bitmap)));
+    this->spawnChild(SkNEW_ARGS(SerializeTask, (*this, fGMFactory(NULL), bitmap)));
     this->spawnChild(SkNEW_ARGS(WriteTask, (*this, bitmap)));
 }
 
diff --git a/dm/DMGpuTask.cpp b/dm/DMGpuTask.cpp
index a002b95..2aceb02 100644
--- a/dm/DMGpuTask.cpp
+++ b/dm/DMGpuTask.cpp
@@ -19,8 +19,8 @@
                  int sampleCount)
     : Task(reporter, taskRunner)
     , fGM(gmFactory(NULL))
-    , fName(underJoin(fGM->shortName(), name))
-    , fExpectations(expectations.get(png(fName).c_str()))
+    , fName(UnderJoin(fGM->shortName(), name))
+    , fExpectations(expectations.get(Png(fName).c_str()))
     , fConfig(config)
     , fContextType(contextType)
     , fSampleCount(sampleCount)
diff --git a/dm/DMReplayTask.cpp b/dm/DMReplayTask.cpp
index 0d6780e..a0ecf5f 100644
--- a/dm/DMReplayTask.cpp
+++ b/dm/DMReplayTask.cpp
@@ -1,54 +1,38 @@
 #include "DMReplayTask.h"
+#include "DMWriteTask.h"
 #include "DMUtil.h"
 
+#include "SkCommandLineFlags.h"
 #include "SkPicture.h"
 
+DEFINE_bool(replay, false, "If true, run picture replay tests.");
+
 namespace DM {
 
-ReplayTask::ReplayTask(const char* suffix,
-                       const Task& parent,
+ReplayTask::ReplayTask(const Task& parent,
                        skiagm::GM* gm,
                        SkBitmap reference)
     : Task(parent)
-    , fName(underJoin(parent.name().c_str(), suffix))
+    , fName(UnderJoin(parent.name().c_str(), "replay"))
     , fGM(gm)
     , fReference(reference)
     {}
 
 void ReplayTask::draw() {
-    SkPicture picture;
-    SkCanvas* canvas = picture.beginRecording(SkScalarCeilToInt(fGM->width()),
-                                              SkScalarCeilToInt(fGM->height()),
-                                              0 /*flags*/);
-
-    canvas->concat(fGM->getInitialTransform());
-    fGM->draw(canvas);
-    canvas->flush();
-
-    picture.endRecording();
+    SkPicture recorded;
+    RecordPicture(fGM.get(), &recorded);
 
     SkBitmap bitmap;
-    bitmap.setConfig(fReference.config(),
-                     SkScalarCeilToInt(fGM->width()),
-                     SkScalarCeilToInt(fGM->height()));
-    bitmap.allocPixels();
-    bitmap.eraseColor(0x00000000);
-
-    SkCanvas replay(bitmap);
-    replay.drawPicture(picture);
-    replay.flush();
-
-    const SkAutoLockPixels mine(bitmap), theirs(fReference);
-    if (bitmap.getSize() != fReference.getSize() ||
-        0 != memcmp(bitmap.getPixels(), fReference.getPixels(), bitmap.getSize()))
-    {
+    SetupBitmap(fReference.config(), fGM.get(), &bitmap);
+    DrawPicture(&recorded, &bitmap);
+    if (!BitmapsEqual(bitmap, fReference)) {
         this->fail();
+        this->spawnChild(SkNEW_ARGS(WriteTask, (*this, bitmap)));
     }
 }
 
 bool ReplayTask::shouldSkip() const {
-    return fGM->getFlags() & skiagm::GM::kGPUOnly_Flag ||
-           fGM->getFlags() & skiagm::GM::kSkipPicture_Flag;
+    return !FLAGS_replay || fGM->getFlags() & skiagm::GM::kSkipPicture_Flag;
 }
 
 }  // namespace DM
diff --git a/dm/DMReplayTask.h b/dm/DMReplayTask.h
index 6a96373..bf90f72 100644
--- a/dm/DMReplayTask.h
+++ b/dm/DMReplayTask.h
@@ -1,14 +1,11 @@
 #ifndef DMReplayTask_DEFINED
 #define DMReplayTask_DEFINED
 
-#include "DMReporter.h"
 #include "DMTask.h"
-#include "DMTaskRunner.h"
 #include "SkBitmap.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 #include "gm.h"
-#include "gm_expectations.h"
 
 // Records a GM through an SkPicture, draws it, and compares against the reference bitmap.
 
@@ -17,8 +14,7 @@
 class ReplayTask : public Task {
 
 public:
-    ReplayTask(const char* name,
-               const Task& parent,
+    ReplayTask(const Task& parent,
                skiagm::GM*,
                SkBitmap reference);
 
diff --git a/dm/DMSerializeTask.cpp b/dm/DMSerializeTask.cpp
new file mode 100644
index 0000000..d71dfdc
--- /dev/null
+++ b/dm/DMSerializeTask.cpp
@@ -0,0 +1,53 @@
+#include "DMSerializeTask.h"
+#include "DMUtil.h"
+#include "DMWriteTask.h"
+
+#include "SkCommandLineFlags.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+
+DEFINE_bool(serialize, false, "If true, run picture serialization tests.");
+
+namespace DM {
+
+SerializeTask::SerializeTask(const Task& parent,
+                             skiagm::GM* gm,
+                             SkBitmap reference)
+    : Task(parent)
+    , fName(UnderJoin(parent.name().c_str(), "serialize"))
+    , fGM(gm)
+    , fReference(reference)
+    {}
+
+static SkData* trivial_bitmap_encoder(size_t* pixelRefOffset, const SkBitmap& bitmap) {
+    if (NULL == bitmap.pixelRef()) {
+        return NULL;
+    }
+    SkData* data = bitmap.pixelRef()->refEncodedData();
+    *pixelRefOffset = bitmap.pixelRefOffset();
+    return data;
+}
+
+void SerializeTask::draw() {
+    SkPicture recorded;
+    RecordPicture(fGM.get(), &recorded);
+
+    SkDynamicMemoryWStream wStream;
+    recorded.serialize(&wStream, &trivial_bitmap_encoder);
+    SkAutoTUnref<SkStream> rStream(wStream.detachAsStream());
+    SkAutoTUnref<SkPicture> reconstructed(SkPicture::CreateFromStream(rStream));
+
+    SkBitmap bitmap;
+    SetupBitmap(fReference.config(), fGM.get(), &bitmap);
+    DrawPicture(reconstructed, &bitmap);
+    if (!BitmapsEqual(bitmap, fReference)) {
+        this->fail();
+        this->spawnChild(SkNEW_ARGS(WriteTask, (*this, bitmap)));
+    }
+}
+
+bool SerializeTask::shouldSkip() const {
+    return !FLAGS_serialize || fGM->getFlags() & skiagm::GM::kSkipPicture_Flag;
+}
+
+}  // namespace DM
diff --git a/dm/DMSerializeTask.h b/dm/DMSerializeTask.h
new file mode 100644
index 0000000..30a4303
--- /dev/null
+++ b/dm/DMSerializeTask.h
@@ -0,0 +1,34 @@
+#ifndef DMSerializeTask_DEFINED
+#define DMSerializeTask_DEFINED
+
+#include "DMTask.h"
+#include "SkBitmap.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "gm.h"
+
+// Record a picture, serialize it, deserialize it, then draw it and compare to reference bitmap.
+
+namespace DM {
+
+class SerializeTask : public Task {
+
+public:
+    SerializeTask(const Task& parent,
+                  skiagm::GM*,
+                  SkBitmap reference);
+
+    virtual void draw() SK_OVERRIDE;
+    virtual bool usesGpu() const SK_OVERRIDE { return false; }
+    virtual bool shouldSkip() const SK_OVERRIDE;
+    virtual SkString name() const SK_OVERRIDE { return fName; }
+
+private:
+    const SkString fName;
+    SkAutoTDelete<skiagm::GM> fGM;
+    const SkBitmap fReference;
+};
+
+}  // namespace DM
+
+#endif  // DMSerializeTask_DEFINED
diff --git a/dm/DMUtil.cpp b/dm/DMUtil.cpp
index dc652eb..a227ca8 100644
--- a/dm/DMUtil.cpp
+++ b/dm/DMUtil.cpp
@@ -1,19 +1,21 @@
 #include "DMUtil.h"
 
+#include "SkPicture.h"
+
 namespace DM {
 
-SkString underJoin(const char* a, const char* b) {
+SkString UnderJoin(const char* a, const char* b) {
     SkString s;
     s.appendf("%s_%s", a, b);
     return s;
 }
 
-SkString png(SkString s) {
+SkString Png(SkString s) {
     s.appendf(".png");
     return s;
 }
 
-bool meetsExpectations(const skiagm::Expectations& expectations, const SkBitmap bitmap) {
+bool MeetsExpectations(const skiagm::Expectations& expectations, const SkBitmap bitmap) {
     if (expectations.ignoreFailure() || expectations.empty()) {
         return true;
     }
@@ -21,4 +23,33 @@
     return expectations.match(digest);
 }
 
+void RecordPicture(skiagm::GM* gm, SkPicture* picture) {
+    SkCanvas* canvas = picture->beginRecording(SkScalarCeilToInt(gm->width()),
+                                               SkScalarCeilToInt(gm->height()),
+                                               0 /*flags*/);
+    canvas->concat(gm->getInitialTransform());
+    gm->draw(canvas);
+    canvas->flush();
+    picture->endRecording();
+}
+
+void SetupBitmap(const SkBitmap::Config config, skiagm::GM* gm, SkBitmap* bitmap) {
+    bitmap->setConfig(config, SkScalarCeilToInt(gm->width()), SkScalarCeilToInt(gm->height()));
+    bitmap->allocPixels();
+    bitmap->eraseColor(0x00000000);
+}
+
+void DrawPicture(SkPicture* picture, SkBitmap* bitmap) {
+    SkASSERT(picture != NULL);
+    SkASSERT(bitmap != NULL);
+    SkCanvas canvas(*bitmap);
+    canvas.drawPicture(*picture);
+    canvas.flush();
+}
+
+bool BitmapsEqual(const SkBitmap& a, const SkBitmap& b) {
+    const SkAutoLockPixels lockA(a), lockB(b);
+    return a.getSize() == b.getSize() && 0 == memcmp(a.getPixels(), b.getPixels(), b.getSize());
+}
+
 }  // namespace DM
diff --git a/dm/DMUtil.h b/dm/DMUtil.h
index c95f933..4dd9dd8 100644
--- a/dm/DMUtil.h
+++ b/dm/DMUtil.h
@@ -9,14 +9,26 @@
 
 namespace DM {
 
-// underJoin("a", "b") -> "a_b"
-SkString underJoin(const char* a, const char* b);
+// UnderJoin("a", "b") -> "a_b"
+SkString UnderJoin(const char* a, const char* b);
 
-// png("a") -> "a.png"
-SkString png(SkString s);
+// Png("a") -> "a.png"
+SkString Png(SkString s);
 
 // Roughly, expectations.match(GmResultDigest(bitmap)), but calculates the digest lazily.
-bool meetsExpectations(const skiagm::Expectations& expectations, const SkBitmap bitmap);
+bool MeetsExpectations(const skiagm::Expectations& expectations, const SkBitmap bitmap);
+
+// Draw gm to picture.
+void RecordPicture(skiagm::GM* gm, SkPicture* picture);
+
+// Prepare bitmap to have gm draw into it with this config.
+void SetupBitmap(const SkBitmap::Config config, skiagm::GM* gm, SkBitmap* bitmap);
+
+// Draw picture to bitmap.
+void DrawPicture(SkPicture* picture, SkBitmap* bitmap);
+
+// Are these identical bitmaps?
+bool BitmapsEqual(const SkBitmap& a, const SkBitmap& b);
 
 }  // namespace DM
 
diff --git a/dm/DMWriteTask.cpp b/dm/DMWriteTask.cpp
index 21a9b55..011b339 100644
--- a/dm/DMWriteTask.cpp
+++ b/dm/DMWriteTask.cpp
@@ -27,7 +27,7 @@
     const SkString dir = SkOSPath::SkPathJoin(root, fConfig.c_str());
     if (!sk_mkdir(root) ||
         !sk_mkdir(dir.c_str())  ||
-        !SkImageEncoder::EncodeFile(png(SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str())).c_str(),
+        !SkImageEncoder::EncodeFile(Png(SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str())).c_str(),
                                     fBitmap,
                                     SkImageEncoder::kPNG_Type,
                                     100/*quality*/))
diff --git a/dm/README b/dm/README
index b64fdfb..dbefe58 100644
--- a/dm/README
+++ b/dm/README
@@ -7,7 +7,6 @@
 
   --deferred / --pipe
   --rtree
-  --serialize
   --tiledGrid