Extends gm to test SkPicture replay and serialization.
Adds usage, fixes coding style violations.
http://codereview.appspot.com/4440063




git-svn-id: http://skia.googlecode.com/svn/trunk@1171 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index cb4e036..952f386 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -3,6 +3,7 @@
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
+#include "SkPicture.h"
 #include "SkStream.h"
 #include "SkRefCnt.h"
 
@@ -13,8 +14,8 @@
 #include "SkDevice.h"
 
 #ifdef SK_SUPPORT_PDF
-	#include "SkPDFDevice.h"
-	#include "SkPDFDocument.h"
+    #include "SkPDFDevice.h"
+    #include "SkPDFDocument.h"
 #endif
 
 using namespace skiagm;
@@ -57,11 +58,16 @@
     return name;
 }
 
-static SkString make_filename(const char path[], const SkString& name, const char suffix[]) {
+static SkString make_filename(const char path[],
+                              const char pathSuffix[],
+                              const SkString& name,
+                              const char suffix[]) {
     SkString filename(path);
-    if (filename.size() && filename[filename.size() - 1] != '/') {
-        filename.append("/");
+    if (filename.endsWith("/")) {
+        filename.remove(filename.size() - 1, 1);
     }
+    filename.append(pathSuffix);
+    filename.append("/");
     filename.appendf("%s.%s", name.c_str(), suffix);
     return filename;
 }
@@ -88,15 +94,15 @@
 }
 
 static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
-	int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
-	int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
-	int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
-	return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
+    int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
+    int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
+    int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
+    return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
 }
 
 static void compute_diff(const SkBitmap& target, const SkBitmap& base,
-						 SkBitmap* diff) {
-	SkAutoLockPixels alp(*diff);
+                         SkBitmap* diff) {
+    SkAutoLockPixels alp(*diff);
 
     const int w = target.width();
     const int h = target.height();
@@ -104,13 +110,13 @@
         for (int x = 0; x < w; x++) {
             SkPMColor c0 = *base.getAddr32(x, y);
             SkPMColor c1 = *target.getAddr32(x, y);
-			SkPMColor d = 0;
-			if (c0 != c1) {
-				d = compute_diff_pmcolor(c0, c1);
-			}
-			*diff->getAddr32(x, y) = d;
-		}
-	}
+            SkPMColor d = 0;
+            if (c0 != c1) {
+                d = compute_diff_pmcolor(c0, c1);
+            }
+            *diff->getAddr32(x, y) = d;
+        }
+    }
 }
 
 static bool compare(const SkBitmap& target, const SkBitmap& base,
@@ -143,18 +149,18 @@
                 SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
                          name.c_str(), x, y, c0, c1);
 
-				if (diff) {
-					diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-					diff->allocPixels();
-					compute_diff(*bm, base, diff);
-				}
+                if (diff) {
+                    diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+                    diff->allocPixels();
+                    compute_diff(*bm, base, diff);
+                }
                 return false;
             }
         }
     }
 
-	// they're equal
-	return true;
+    // they're equal
+    return true;
 }
 
 static bool write_pdf(const SkString& path, const SkDynamicMemoryWStream& pdf) {
@@ -168,11 +174,245 @@
   kPDF_Backend,
 };
 
-static const struct {
-	SkBitmap::Config	fConfig;
+struct ConfigData {
+    SkBitmap::Config    fConfig;
     Backend             fBackend;
-	const char*			fName;
-} gRec[] = {
+    const char*         fName;
+};
+
+/// Returns true if processing should continue, false to skip the
+/// remainder of this config for this GM.
+//@todo thudson 22 April 2011 - could refactor this to take in
+// a factory to generate the context, always call readPixels()
+// (logically a noop for rasters, if wasted time), and thus collapse the
+// GPU special case and also let this be used for SkPicture testing.
+static void setup_bitmap(const ConfigData& gRec, SkISize& size,
+                         SkBitmap* bitmap) {
+    bitmap->setConfig(gRec.fConfig, size.width(), size.height());
+    bitmap->allocPixels();
+    bitmap->eraseColor(0);
+}
+
+static bool generate_image(GM* gm, const ConfigData& gRec,
+                           GrContext* context,
+                           SkBitmap& bitmap) {
+    SkISize size (gm->getISize());
+    setup_bitmap(gRec, size, &bitmap);
+    SkCanvas canvas(bitmap);
+
+    if (gRec.fBackend == kRaster_Backend) {
+        gm->draw(&canvas);
+    } else {  // GPU
+        if (NULL == context) {
+            return false;
+        }
+        SkGpuCanvas gc(context,
+                       SkGpuDevice::Current3DApiRenderTarget());
+        gc.setDevice(gc.createDevice(bitmap.config(),
+                                     bitmap.width(),
+                                     bitmap.height(),
+                                     bitmap.isOpaque(),
+                                     false))->unref();
+        gm->draw(&gc);
+        gc.readPixels(&bitmap); // overwrite our previous allocation
+    }
+    return true;
+}
+
+static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
+                                        SkPicture* pict, SkBitmap* bitmap) {
+    SkISize size = gm->getISize();
+    setup_bitmap(gRec, size, bitmap);
+    SkCanvas canvas(*bitmap);
+    canvas.drawPicture(*pict);
+}
+
+static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
+#ifdef SK_SUPPORT_PDF
+    SkISize size = gm->getISize();
+    SkMatrix identity;
+    identity.reset();
+    SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height(),
+                                       identity);
+    SkAutoUnref aur(dev);
+
+    SkCanvas c(dev);
+    gm->draw(&c);
+
+    SkPDFDocument doc;
+    doc.appendPage(dev);
+    doc.emitPDF(&pdf);
+#endif
+}
+
+static void write_reference_image(const ConfigData& gRec,
+                                  const char writePath [],
+                                  const char writePathSuffix [],
+                                  const SkString& name,
+                                  SkBitmap& bitmap,
+                                  SkDynamicMemoryWStream* pdf) {
+    SkString path;
+    bool success = false;
+    if (gRec.fBackend != kPDF_Backend) {
+        path = make_filename(writePath, writePathSuffix, name, "png");
+        success = write_bitmap(path, bitmap);
+    } else if (pdf) {
+        path = make_filename(writePath, writePathSuffix, name, "pdf");
+        success = write_pdf(path, *pdf);
+    }
+    if (!success) {
+        fprintf(stderr, "FAILED to write %s\n", path.c_str());
+    }
+}
+
+static void compare_to_reference_image(const char readPath [],
+                                       const SkString& name,
+                                       SkBitmap &bitmap,
+                                       const char diffPath []) {
+    SkString path = make_filename(readPath, "", name, "png");
+    SkBitmap orig;
+    bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
+                        SkBitmap::kARGB_8888_Config,
+                        SkImageDecoder::kDecodePixels_Mode, NULL);
+    if (success) {
+        SkBitmap diffBitmap;
+        success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
+        if (!success && diffPath) {
+            SkString diffName = make_filename(diffPath, "", name, ".diff.png");
+            fprintf(stderr, "Writing %s\n", diffName.c_str());
+            write_bitmap(diffName, diffBitmap);
+        }
+    } else {
+        fprintf(stderr, "FAILED to read %s\n", path.c_str());
+    }
+
+}
+
+static void handle_test_results(GM* gm,
+                                const ConfigData& gRec,
+                                const char writePath [],
+                                const char readPath [],
+                                const char diffPath [],
+                                const char writePathSuffix [],
+                                SkBitmap& bitmap,
+                                SkDynamicMemoryWStream* pdf) {
+    SkString name = make_name(gm->shortName(), gRec.fName);
+
+    if (writePath) {
+        write_reference_image(gRec, writePath, writePathSuffix,
+                              name, bitmap, pdf);
+    // TODO: Figure out a way to compare PDFs.
+    } else if (readPath && gRec.fBackend != kPDF_Backend) {
+        compare_to_reference_image(readPath, name, bitmap,
+                                   diffPath);
+    }
+}
+
+static SkPicture* generate_new_picture(GM* gm) {
+    // Pictures are refcounted so must be on heap
+    SkPicture* pict = new SkPicture;
+    SkCanvas* cv = pict->beginRecording(1000, 1000);
+    gm->draw(cv);
+    pict->endRecording();
+
+    return pict;
+}
+
+static SkPicture* stream_to_new_picture(const SkPicture& src) {
+
+    // To do in-memory commiunications with a stream, we need to:
+    // * create a dynamic memory stream
+    // * copy it into a buffer
+    // * create a read stream from it
+    // ?!?!
+
+    SkDynamicMemoryWStream storage;
+    src.serialize(&storage);
+
+    int streamSize = storage.getOffset();
+    SkAutoMalloc dstStorage(streamSize);
+    void* dst = dstStorage.get();
+    //char* dst = new char [streamSize];
+    //@todo thudson 22 April 2011 when can we safely delete [] dst?
+    storage.copyTo(dst);
+    SkMemoryStream pictReadback(dst, streamSize);
+    SkPicture* retval = new SkPicture (&pictReadback);
+    return retval;
+}
+
+// Test: draw into a bitmap or pdf.
+// Depending on flags, possibly compare to an expected image
+// and possibly output a diff image if it fails to match.
+static void test_drawing(GM* gm,
+                         const ConfigData& gRec,
+                         const char writePath [],
+                         const char readPath [],
+                         const char diffPath [],
+                         GrContext* context) {
+    SkBitmap bitmap;
+    SkDynamicMemoryWStream pdf;
+
+    if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend) {
+        if (!generate_image(gm, gRec, context, bitmap)) {
+            return;
+        }
+    }
+    // TODO: Figure out a way to compare PDFs.
+    if (gRec.fBackend == kPDF_Backend && writePath) {
+        generate_pdf(gm, pdf);
+    }
+    handle_test_results(gm, gRec, writePath, readPath, diffPath,
+                        "", bitmap, &pdf);
+}
+
+static void test_picture_playback(GM* gm,
+                                  const ConfigData& gRec,
+                                  const char writePath [],
+                                  const char readPath [],
+                                  const char diffPath []) {
+    SkPicture* pict = generate_new_picture(gm);
+    SkAutoUnref aur(pict);
+
+    //@todo thudson 22 April 2011 wrap GM with a proxy so we can
+    // pass this pict to generate_image()?
+    if (kRaster_Backend == gRec.fBackend) {
+        SkBitmap bitmap;
+        generate_image_from_picture(gm, gRec, pict, &bitmap);
+        handle_test_results(gm, gRec, writePath, readPath, diffPath,
+                            "-pict", bitmap, NULL);
+    }
+}
+
+static void test_picture_serialization(GM* gm,
+                                       const ConfigData& gRec,
+                                       const char writePath [],
+                                       const char readPath [],
+                                       const char diffPath []) {
+    SkPicture* pict = generate_new_picture(gm);
+    SkAutoUnref aurp(pict);
+    SkPicture* repict = stream_to_new_picture(*pict);
+    SkAutoUnref aurr(repict);
+
+    //@todo thudson 22 April 2011 wrap GM with a proxy so we can
+    // pass this pict to generate_image()?
+    if (kRaster_Backend == gRec.fBackend) {
+        SkBitmap bitmap;
+        generate_image_from_picture(gm, gRec, repict, &bitmap);
+        handle_test_results(gm, gRec, writePath, readPath, diffPath,
+                            "-replay", bitmap, NULL);
+    }
+}
+
+static void usage(const char * argv0) {
+    SkDebugf("%s [-w writePath] [-r readPath] [-d diffPath]\n", argv0);
+    SkDebugf("    writePath: directory to write rendered images in.\n");
+    SkDebugf("    readPath: directory to read reference images from;\n"
+             "        reports if any pixels mismatch between reference and newly rendered\n");
+    SkDebugf("    diffPath: directory to write difference images in.\n");
+}
+
+static const ConfigData gRec[] = {
     { SkBitmap::kARGB_8888_Config, kRaster_Backend, "8888" },
     { SkBitmap::kARGB_4444_Config, kRaster_Backend, "4444" },
     { SkBitmap::kRGB_565_Config,   kRaster_Backend, "565" },
@@ -182,13 +422,14 @@
 #endif
 };
 
-int main (int argc, char * const argv[]) {
+int main(int argc, char * const argv[]) {
     SkAutoGraphics ag;
 
     const char* writePath = NULL;   // if non-null, where we write the originals
     const char* readPath = NULL;    // if non-null, were we read from to compare
-	const char* diffPath = NULL;	// if non-null, where we write our diffs (from compare)
+    const char* diffPath = NULL;    // if non-null, where we write our diffs (from compare)
 
+    const char* const commandName = argv[0];
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
         if (strcmp(*argv, "-w") == 0) {
@@ -202,12 +443,19 @@
                 readPath = *argv;
             }
         } else if (strcmp(*argv, "-d") == 0) {
-			argv++;
+            argv++;
             if (argv < stop && **argv) {
                 diffPath = *argv;
             }
-		}
-	}
+        } else {
+          usage(commandName);
+          return 0;
+        }
+    }
+    if (argv != stop) {
+      usage(commandName);
+      return 0;
+    }
 
     // setup a GL context for drawing offscreen
     GrContext* context = NULL;
@@ -226,89 +474,20 @@
     }
 
     while ((gm = iter.next()) != NULL) {
-		SkISize size = gm->getISize();
+        SkISize size = gm->getISize();
         SkDebugf("drawing... %s [%d %d]\n", gm->shortName(),
                  size.width(), size.height());
 
-		SkBitmap bitmap;
-        SkDynamicMemoryWStream pdf;
-		for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
-            if (gRec[i].fBackend == kRaster_Backend ||
-                    gRec[i].fBackend == kGPU_Backend) {
-                bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
-                bitmap.allocPixels();
-                bitmap.eraseColor(0);
-                SkCanvas canvas(bitmap);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+            test_drawing(gm, gRec[i],
+                         writePath, readPath, diffPath, context);
 
-                if (gRec[i].fBackend == kRaster_Backend) {
-                    gm->draw(&canvas);
-                } else {  // GPU
-                    if (NULL == context) {
-                        continue;
-                    }
-                    SkGpuCanvas gc(context,
-                                   SkGpuDevice::Current3DApiRenderTarget());
-                    gc.setDevice(gc.createDevice(bitmap.config(),
-                                                 bitmap.width(),
-                                                 bitmap.height(),
-                                                 bitmap.isOpaque(),
-                                                 false))->unref();
-                    gm->draw(&gc);
-                    gc.readPixels(&bitmap); // overwrite our previous allocation
-                }
-            }
-            // TODO: Figure out a way to compare PDFs.
-            if (gRec[i].fBackend == kPDF_Backend && writePath) {
-#ifdef SK_SUPPORT_PDF
-                SkISize size = gm->getISize();
-                SkMatrix identity;
-                identity.reset();
-                SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height(),
-                                                   identity);
-                SkAutoUnref aur(dev);
+            test_picture_playback(gm, gRec[i],
+                                  writePath, readPath, diffPath);
 
-                SkCanvas c(dev);
-                gm->draw(&c);
-
-                SkPDFDocument doc;
-                doc.appendPage(dev);
-                doc.emitPDF(&pdf);
-#endif
-            }
-            SkString name = make_name(gm->shortName(), gRec[i].fName);
-
-            if (writePath) {
-                SkString path;
-                bool success;
-                if (gRec[i].fBackend != kPDF_Backend) {
-                    path = make_filename(writePath, name, "png");
-                    success = write_bitmap(path, bitmap);
-                } else {
-                    path = make_filename(writePath, name, "pdf");
-                    success = write_pdf(path, pdf);
-                }
-                if (!success)
-                    fprintf(stderr, "FAILED to write %s\n", path.c_str());
-            // TODO: Figure out a way to compare PDFs.
-            } else if (readPath && gRec[i].fBackend != kPDF_Backend) {
-                SkString path = make_filename(readPath, name, "png");
-                SkBitmap orig;
-                bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
-                                    SkBitmap::kARGB_8888_Config,
-                                    SkImageDecoder::kDecodePixels_Mode, NULL);
-                if (success) {
-					SkBitmap diffBitmap;
-                    success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL);
-					if (!success && diffPath) {
-						SkString diffName = make_filename(diffPath, name, ".diff.png");
-						fprintf(stderr, "Writing %s\n", diffName.c_str());
-						write_bitmap(diffName, diffBitmap);
-					}
-                } else {
-                    fprintf(stderr, "FAILED to read %s\n", path.c_str());
-                }
-            }
-		}
+            test_picture_serialization(gm, gRec[i],
+                                       writePath, readPath, diffPath);
+        }
         SkDELETE(gm);
     }
     return 0;
@@ -322,7 +501,5 @@
 GM::~GM() {}
 
 void GM::draw(SkCanvas* canvas) {
-	this->onDraw(canvas);
+    this->onDraw(canvas);
 }
-
-