object-ify gmmain.cpp (no functional change) to ease planned changes later
Review URL: https://codereview.appspot.com/6801043

git-svn-id: http://skia.googlecode.com/svn/trunk@6147 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 956f687..bf03214 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -11,6 +11,7 @@
 #include "SkData.h"
 #include "SkDeferredCanvas.h"
 #include "SkDevice.h"
+#include "SkDrawFilter.h"
 #include "SkGPipe.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
@@ -105,144 +106,6 @@
     const GMRegistry* fReg;
 };
 
-static SkString make_name(const char shortName[], const char configName[]) {
-    SkString name(shortName);
-    name.appendf("_%s", configName);
-    return name;
-}
-
-static SkString make_filename(const char path[],
-                              const char pathSuffix[],
-                              const SkString& name,
-                              const char suffix[]) {
-    SkString filename(path);
-    if (filename.endsWith(PATH_SEPARATOR)) {
-        filename.remove(filename.size() - 1, 1);
-    }
-    filename.append(pathSuffix);
-    filename.append(PATH_SEPARATOR);
-    filename.appendf("%s.%s", name.c_str(), suffix);
-    return filename;
-}
-
-/* since PNG insists on unpremultiplying our alpha, we take no precision chances
-    and force all pixels to be 100% opaque, otherwise on compare we may not get
-    a perfect match.
- */
-static void force_all_opaque(const SkBitmap& bitmap) {
-    SkAutoLockPixels lock(bitmap);
-    for (int y = 0; y < bitmap.height(); y++) {
-        for (int x = 0; x < bitmap.width(); x++) {
-            *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
-        }
-    }
-}
-
-static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
-    SkBitmap copy;
-    bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-    force_all_opaque(copy);
-    return SkImageEncoder::EncodeFile(path.c_str(), copy,
-                                      SkImageEncoder::kPNG_Type, 100);
-}
-
-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));
-}
-
-static void compute_diff(const SkBitmap& target, const SkBitmap& base,
-                         SkBitmap* diff) {
-    SkAutoLockPixels alp(*diff);
-
-    const int w = target.width();
-    const int h = target.height();
-    for (int y = 0; y < h; y++) {
-        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;
-        }
-    }
-}
-
-// Compares "target" and "base" bitmaps, returning the result (ERROR_NONE
-// if the two bitmaps are identical).
-//
-// If a "diff" bitmap is passed in, pixel diffs (if any) will be written
-// into it.
-//
-// The "name" and "renderModeDescriptor" arguments are only used in the debug
-// output.
-static ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base,
-                             const SkString& name,
-                             const char* renderModeDescriptor,
-                             SkBitmap* diff) {
-    SkBitmap copy;
-    const SkBitmap* bm = &target;
-    if (target.config() != SkBitmap::kARGB_8888_Config) {
-        target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-        bm = &copy;
-    }
-    SkBitmap baseCopy;
-    const SkBitmap* bp = &base;
-    if (base.config() != SkBitmap::kARGB_8888_Config) {
-        base.copyTo(&baseCopy, SkBitmap::kARGB_8888_Config);
-        bp = &baseCopy;
-    }
-
-    force_all_opaque(*bm);
-    force_all_opaque(*bp);
-
-    const int w = bm->width();
-    const int h = bm->height();
-    if (w != bp->width() || h != bp->height()) {
-        SkDebugf(
-"---- %s dimensions mismatch for %s base [%d %d] current [%d %d]\n",
-                 renderModeDescriptor, name.c_str(),
-                 bp->width(), bp->height(), w, h);
-        return ERROR_DIMENSION_MISMATCH;
-    }
-
-    SkAutoLockPixels bmLock(*bm);
-    SkAutoLockPixels baseLock(*bp);
-
-    for (int y = 0; y < h; y++) {
-        for (int x = 0; x < w; x++) {
-            SkPMColor c0 = *bp->getAddr32(x, y);
-            SkPMColor c1 = *bm->getAddr32(x, y);
-            if (c0 != c1) {
-                SkDebugf(
-"----- %s pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
-                         renderModeDescriptor, name.c_str(), x, y, c0, c1);
-
-                if (diff) {
-                    diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-                    diff->allocPixels();
-                    compute_diff(*bm, *bp, diff);
-                }
-                return ERROR_PIXEL_MISMATCH;
-            }
-        }
-    }
-
-    // they're equal
-    return ERROR_NONE;
-}
-
-static bool write_document(const SkString& path,
-                           const SkDynamicMemoryWStream& document) {
-    SkFILEWStream stream(path.c_str());
-    SkAutoDataUnref data(document.copyToData());
-    return stream.writeData(data.get());
-}
-
 enum Backend {
   kRaster_Backend,
   kGPU_Backend,
@@ -268,20 +131,6 @@
     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);
-}
-
-#include "SkDrawFilter.h"
 class BWTextDrawFilter : public SkDrawFilter {
 public:
     virtual void filter(SkPaint*, Type) SK_OVERRIDE;
@@ -292,334 +141,6 @@
     }
 }
 
-static void installFilter(SkCanvas* canvas) {
-    if (gForceBWtext) {
-        canvas->setDrawFilter(new BWTextDrawFilter)->unref();
-    }
-}
-
-static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF = false) {
-    SkAutoCanvasRestore acr(canvas, true);
-
-    if (!isPDF) {
-        canvas->concat(gm->getInitialTransform());
-    }
-    installFilter(canvas);
-    gm->draw(canvas);
-    canvas->setDrawFilter(NULL);
-}
-
-static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
-                                    GrContext* context,
-                                    GrRenderTarget* rt,
-                                    SkBitmap* bitmap,
-                                    bool deferred) {
-    SkISize size (gm->getISize());
-    setup_bitmap(gRec, size, bitmap);
-
-    SkAutoTUnref<SkCanvas> canvas;
-
-    if (gRec.fBackend == kRaster_Backend) {
-        SkAutoTUnref<SkDevice> device(new SkDevice(*bitmap));
-        if (deferred) {
-            canvas.reset(new SkDeferredCanvas(device));
-        } else {
-            canvas.reset(new SkCanvas(device));
-        }
-        invokeGM(gm, canvas);
-        canvas->flush();
-    }
-#if SK_SUPPORT_GPU
-    else {  // GPU
-        if (NULL == context) {
-            return ERROR_NO_GPU_CONTEXT;
-        }
-        SkAutoTUnref<SkDevice> device(new SkGpuDevice(context, rt));
-        if (deferred) {
-            canvas.reset(new SkDeferredCanvas(device));
-        } else {
-            canvas.reset(new SkCanvas(device));
-        }
-        invokeGM(gm, canvas);
-        // the device is as large as the current rendertarget, so we explicitly
-        // only readback the amount we expect (in size)
-        // overwrite our previous allocation
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
-                                                       size.fHeight);
-        canvas->readPixels(bitmap, 0, 0);
-    }
-#endif
-    return ERROR_NONE;
-}
-
-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);
-    installFilter(&canvas);
-    canvas.drawPicture(*pict);
-}
-
-static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
-#ifdef SK_SUPPORT_PDF
-    SkMatrix initialTransform = gm->getInitialTransform();
-    SkISize pageSize = gm->getISize();
-    SkPDFDevice* dev = NULL;
-    if (initialTransform.isIdentity()) {
-        dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
-    } else {
-        SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
-                                        SkIntToScalar(pageSize.height()));
-        initialTransform.mapRect(&content);
-        content.intersect(0, 0, SkIntToScalar(pageSize.width()),
-                                SkIntToScalar(pageSize.height()));
-        SkISize contentSize =
-            SkISize::Make(SkScalarRoundToInt(content.width()),
-                          SkScalarRoundToInt(content.height()));
-        dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
-    }
-    SkAutoUnref aur(dev);
-
-    SkCanvas c(dev);
-    invokeGM(gm, &c, true);
-
-    SkPDFDocument doc;
-    doc.appendPage(dev);
-    doc.emitPDF(&pdf);
-#endif
-}
-
-static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
-#ifdef SK_SUPPORT_XPS
-    SkISize size = gm->getISize();
-
-    SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
-                                   SkIntToScalar(size.height()));
-    static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
-    static const SkScalar upm = 72 * inchesPerMeter;
-    SkVector unitsPerMeter = SkPoint::Make(upm, upm);
-    static const SkScalar ppm = 200 * inchesPerMeter;
-    SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
-
-    SkXPSDevice* dev = new SkXPSDevice();
-    SkAutoUnref aur(dev);
-
-    SkCanvas c(dev);
-    dev->beginPortfolio(&xps);
-    dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
-    invokeGM(gm, &c);
-    dev->endSheet();
-    dev->endPortfolio();
-
-#endif
-}
-
-static ErrorBitfield write_reference_image(const ConfigData& gRec,
-                                           const char writePath [],
-                                           const char renderModeDescriptor [],
-                                           const SkString& name,
-                                           SkBitmap& bitmap,
-                                           SkDynamicMemoryWStream* document) {
-    SkString path;
-    bool success = false;
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend ||
-        (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
-
-        path = make_filename(writePath, renderModeDescriptor, name, "png");
-        success = write_bitmap(path, bitmap);
-    }
-    if (kPDF_Backend == gRec.fBackend) {
-        path = make_filename(writePath, renderModeDescriptor, name, "pdf");
-        success = write_document(path, *document);
-    }
-    if (kXPS_Backend == gRec.fBackend) {
-        path = make_filename(writePath, renderModeDescriptor, name, "xps");
-        success = write_document(path, *document);
-    }
-    if (success) {
-        return ERROR_NONE;
-    } else {
-        fprintf(stderr, "FAILED to write %s\n", path.c_str());
-        return ERROR_WRITING_REFERENCE_IMAGE;
-    }
-}
-
-// Compares bitmap "bitmap" to "referenceBitmap"; if they are
-// different, writes out "bitmap" (in PNG format) within the diffPath subdir.
-//
-// Returns the ErrorBitfield from compare(), describing any differences
-// between "bitmap" and "referenceBitmap" (or ERROR_NONE if there are none).
-static ErrorBitfield compare_to_reference_image_in_memory(const SkString& name,
-                                                SkBitmap &bitmap,
-                                                const SkBitmap& referenceBitmap,
-                                                const char diffPath [],
-                                                const char renderModeDescriptor []) {
-    ErrorBitfield errors;
-    SkBitmap diffBitmap;
-    errors = compare(bitmap, referenceBitmap, name, renderModeDescriptor,
-                     diffPath ? &diffBitmap : NULL);
-    if ((ERROR_NONE != errors) && diffPath) {
-        // write out the generated image
-        SkString genName = make_filename(diffPath, "", name, "png");
-        if (!write_bitmap(genName, bitmap)) {
-            errors |= ERROR_WRITING_REFERENCE_IMAGE;
-        }
-    }
-    return errors;
-}
-
-// Compares bitmap "bitmap" to a reference bitmap read from disk; if they are
-// different, writes out "bitmap" (in PNG format) within the diffPath subdir.
-//
-// Returns a description of the difference between "bitmap" and the reference
-// bitmap, or ERROR_READING_REFERENCE_IMAGE if unable to read the reference
-// bitmap from disk.
-static ErrorBitfield compare_to_reference_image_on_disk(const char readPath [],
-                                                const SkString& name,
-                                                SkBitmap &bitmap,
-                                                const char diffPath [],
-                                                const char renderModeDescriptor []) {
-    SkString path = make_filename(readPath, "", name, "png");
-    SkBitmap referenceBitmap;
-    if (SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
-                                   SkBitmap::kARGB_8888_Config,
-                                   SkImageDecoder::kDecodePixels_Mode, NULL)) {
-        return compare_to_reference_image_in_memory(name, bitmap,
-                                                    referenceBitmap, diffPath,
-                                                    renderModeDescriptor);
-    } else {
-        if (gNotifyMissingReadReference) {
-            fprintf(stderr, "FAILED to read %s\n", path.c_str());
-        }
-        return ERROR_READING_REFERENCE_IMAGE;
-    }
-}
-
-// NOTE: As far as I can tell, this function is NEVER called with a
-// non-blank renderModeDescriptor, EXCEPT with readPath and writePath are
-// both NULL (and thus no images are read from or written to disk).
-// So I don't trust that the renderModeDescriptor is being used for
-// anything other than debug output these days.
-static ErrorBitfield handle_test_results(GM* gm,
-                                         const ConfigData& gRec,
-                                         const char writePath [],
-                                         const char readPath [],
-                                         const char diffPath [],
-                                         const char renderModeDescriptor [],
-                                         SkBitmap& bitmap,
-                                         SkDynamicMemoryWStream* pdf,
-                                         const SkBitmap* referenceBitmap) {
-    SkString name = make_name(gm->shortName(), gRec.fName);
-    ErrorBitfield retval = ERROR_NONE;
-
-    if (readPath && (gRec.fFlags & kRead_ConfigFlag)) {
-        retval |= compare_to_reference_image_on_disk(readPath, name, bitmap,
-                                                     diffPath, renderModeDescriptor);
-    }
-    if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
-        retval |= write_reference_image(gRec, writePath, renderModeDescriptor,
-                                        name, bitmap, pdf);
-    }
-    if (referenceBitmap) {
-        retval |= compare_to_reference_image_in_memory(name, bitmap,
-                                                       *referenceBitmap, diffPath,
-                                                       renderModeDescriptor);
-    }
-    return retval;
-}
-
-static SkPicture* generate_new_picture(GM* gm) {
-    // Pictures are refcounted so must be on heap
-    SkPicture* pict = new SkPicture;
-    SkISize size = gm->getISize();
-    SkCanvas* cv = pict->beginRecording(size.width(), size.height());
-    invokeGM(gm, 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 ErrorBitfield test_drawing(GM* gm,
-                                  const ConfigData& gRec,
-                                  const char writePath [],
-                                  const char readPath [],
-                                  const char diffPath [],
-                                  GrContext* context,
-                                  GrRenderTarget* rt,
-                                  SkBitmap* bitmap) {
-    SkDynamicMemoryWStream document;
-
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend) {
-        // Early exit if we can't generate the image.
-        ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
-            false);
-        if (ERROR_NONE != errors) {
-            return errors;
-        }
-    } else if (gRec.fBackend == kPDF_Backend) {
-        generate_pdf(gm, document);
-#if CAN_IMAGE_PDF
-        SkAutoDataUnref data(document.copyToData());
-        SkMemoryStream stream(data->data(), data->size());
-        SkPDFDocumentToBitmap(&stream, bitmap);
-#endif
-    } else if (gRec.fBackend == kXPS_Backend) {
-        generate_xps(gm, document);
-    }
-    return handle_test_results(gm, gRec, writePath, readPath, diffPath,
-                               "", *bitmap, &document, NULL);
-}
-
-static ErrorBitfield test_deferred_drawing(GM* gm,
-                         const ConfigData& gRec,
-                         const SkBitmap& referenceBitmap,
-                         const char diffPath [],
-                         GrContext* context,
-                         GrRenderTarget* rt) {
-    SkDynamicMemoryWStream document;
-
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend) {
-        SkBitmap bitmap;
-        // Early exit if we can't generate the image, but this is
-        // expected in some cases, so don't report a test failure.
-        if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
-            return ERROR_NONE;
-        }
-        return handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                   "-deferred", bitmap, NULL, &referenceBitmap);
-    }
-    return ERROR_NONE;
-}
-
 struct PipeFlagComboData {
     const char* name;
     uint32_t flags;
@@ -632,63 +153,544 @@
         | SkGPipeWriter::kSharedAddressSpace_Flag }
 };
 
-static ErrorBitfield test_pipe_playback(GM* gm,
-                                        const ConfigData& gRec,
-                                        const SkBitmap& referenceBitmap,
-                                        const char readPath [],
-                                        const char diffPath []) {
-    ErrorBitfield errors = ERROR_NONE;
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
-        SkBitmap bitmap;
-        SkISize size = gm->getISize();
-        setup_bitmap(gRec, size, &bitmap);
-        SkCanvas canvas(bitmap);
-        PipeController pipeController(&canvas);
-        SkGPipeWriter writer;
-        SkCanvas* pipeCanvas = writer.startRecording(
-            &pipeController, gPipeWritingFlagCombos[i].flags);
-        invokeGM(gm, pipeCanvas);
-        writer.endRecording();
-        SkString string("-pipe");
-        string.append(gPipeWritingFlagCombos[i].name);
-        errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                      string.c_str(), bitmap, NULL,
-                                      &referenceBitmap);
-        if (errors != ERROR_NONE) {
-            break;
-        }
-    }
-    return errors;
-}
 
-static ErrorBitfield test_tiled_pipe_playback(GM* gm,
-                                        const ConfigData& gRec,
-                                        const SkBitmap& referenceBitmap,
-                                        const char readPath [],
-                                        const char diffPath []) {
-    ErrorBitfield errors = ERROR_NONE;
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
-        SkBitmap bitmap;
-        SkISize size = gm->getISize();
-        setup_bitmap(gRec, size, &bitmap);
-        SkCanvas canvas(bitmap);
-        TiledPipeController pipeController(bitmap);
-        SkGPipeWriter writer;
-        SkCanvas* pipeCanvas = writer.startRecording(
-            &pipeController, gPipeWritingFlagCombos[i].flags);
-        invokeGM(gm, pipeCanvas);
-        writer.endRecording();
-        SkString string("-tiled pipe");
-        string.append(gPipeWritingFlagCombos[i].name);
-        errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                      string.c_str(), bitmap, NULL,
-                                      &referenceBitmap);
-        if (errors != ERROR_NONE) {
-            break;
+class GMMain {
+public:
+    static SkString make_name(const char shortName[], const char configName[]) {
+        SkString name(shortName);
+        name.appendf("_%s", configName);
+        return name;
+    }
+
+    static SkString make_filename(const char path[],
+                                  const char pathSuffix[],
+                                  const SkString& name,
+                                  const char suffix[]) {
+        SkString filename(path);
+        if (filename.endsWith(PATH_SEPARATOR)) {
+            filename.remove(filename.size() - 1, 1);
+        }
+        filename.append(pathSuffix);
+        filename.append(PATH_SEPARATOR);
+        filename.appendf("%s.%s", name.c_str(), suffix);
+        return filename;
+    }
+
+    /* since PNG insists on unpremultiplying our alpha, we take no
+       precision chances and force all pixels to be 100% opaque,
+       otherwise on compare we may not get a perfect match.
+    */
+    static void force_all_opaque(const SkBitmap& bitmap) {
+        SkAutoLockPixels lock(bitmap);
+        for (int y = 0; y < bitmap.height(); y++) {
+            for (int x = 0; x < bitmap.width(); x++) {
+                *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
+            }
         }
     }
-    return errors;
-}
+
+    static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
+        SkBitmap copy;
+        bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+        force_all_opaque(copy);
+        return SkImageEncoder::EncodeFile(path.c_str(), copy,
+                                          SkImageEncoder::kPNG_Type, 100);
+    }
+
+    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));
+    }
+
+    static void compute_diff(const SkBitmap& target, const SkBitmap& base,
+                             SkBitmap* diff) {
+        SkAutoLockPixels alp(*diff);
+
+        const int w = target.width();
+        const int h = target.height();
+        for (int y = 0; y < h; y++) {
+            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;
+            }
+        }
+    }
+
+    // Compares "target" and "base" bitmaps, returning the result
+    // (ERROR_NONE if the two bitmaps are identical).
+    //
+    // If a "diff" bitmap is passed in, pixel diffs (if any) will be written
+    // into it.
+    //
+    // The "name" and "renderModeDescriptor" arguments are only used
+    // in the debug output.
+    static ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base,
+                                 const SkString& name,
+                                 const char* renderModeDescriptor,
+                                 SkBitmap* diff) {
+        SkBitmap copy;
+        const SkBitmap* bm = &target;
+        if (target.config() != SkBitmap::kARGB_8888_Config) {
+            target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+            bm = &copy;
+        }
+        SkBitmap baseCopy;
+        const SkBitmap* bp = &base;
+        if (base.config() != SkBitmap::kARGB_8888_Config) {
+            base.copyTo(&baseCopy, SkBitmap::kARGB_8888_Config);
+            bp = &baseCopy;
+        }
+
+        force_all_opaque(*bm);
+        force_all_opaque(*bp);
+
+        const int w = bm->width();
+        const int h = bm->height();
+        if (w != bp->width() || h != bp->height()) {
+            SkDebugf(
+                     "---- %s dimensions mismatch for %s base [%d %d] current [%d %d]\n",
+                     renderModeDescriptor, name.c_str(),
+                     bp->width(), bp->height(), w, h);
+            return ERROR_DIMENSION_MISMATCH;
+        }
+
+        SkAutoLockPixels bmLock(*bm);
+        SkAutoLockPixels baseLock(*bp);
+
+        for (int y = 0; y < h; y++) {
+            for (int x = 0; x < w; x++) {
+                SkPMColor c0 = *bp->getAddr32(x, y);
+                SkPMColor c1 = *bm->getAddr32(x, y);
+                if (c0 != c1) {
+                    SkDebugf(
+                             "----- %s pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
+                             renderModeDescriptor, name.c_str(), x, y, c0, c1);
+
+                    if (diff) {
+                        diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+                        diff->allocPixels();
+                        compute_diff(*bm, *bp, diff);
+                    }
+                    return ERROR_PIXEL_MISMATCH;
+                }
+            }
+        }
+
+        // they're equal
+        return ERROR_NONE;
+    }
+
+    static bool write_document(const SkString& path,
+                               const SkDynamicMemoryWStream& document) {
+        SkFILEWStream stream(path.c_str());
+        SkAutoDataUnref data(document.copyToData());
+        return stream.writeData(data.get());
+    }
+
+    /// 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 void installFilter(SkCanvas* canvas) {
+        if (gForceBWtext) {
+            canvas->setDrawFilter(new BWTextDrawFilter)->unref();
+        }
+    }
+
+    static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF = false) {
+        SkAutoCanvasRestore acr(canvas, true);
+
+        if (!isPDF) {
+            canvas->concat(gm->getInitialTransform());
+        }
+        installFilter(canvas);
+        gm->draw(canvas);
+        canvas->setDrawFilter(NULL);
+    }
+
+    static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
+                                        GrContext* context,
+                                        GrRenderTarget* rt,
+                                        SkBitmap* bitmap,
+                                        bool deferred) {
+        SkISize size (gm->getISize());
+        setup_bitmap(gRec, size, bitmap);
+
+        SkAutoTUnref<SkCanvas> canvas;
+
+        if (gRec.fBackend == kRaster_Backend) {
+            SkAutoTUnref<SkDevice> device(new SkDevice(*bitmap));
+            if (deferred) {
+                canvas.reset(new SkDeferredCanvas(device));
+            } else {
+                canvas.reset(new SkCanvas(device));
+            }
+            invokeGM(gm, canvas);
+            canvas->flush();
+        }
+#if SK_SUPPORT_GPU
+        else {  // GPU
+            if (NULL == context) {
+                return ERROR_NO_GPU_CONTEXT;
+            }
+            SkAutoTUnref<SkDevice> device(new SkGpuDevice(context, rt));
+            if (deferred) {
+                canvas.reset(new SkDeferredCanvas(device));
+            } else {
+                canvas.reset(new SkCanvas(device));
+            }
+            invokeGM(gm, canvas);
+            // the device is as large as the current rendertarget, so
+            // we explicitly only readback the amount we expect (in
+            // size) overwrite our previous allocation
+            bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
+                              size.fHeight);
+            canvas->readPixels(bitmap, 0, 0);
+        }
+#endif
+        return ERROR_NONE;
+    }
+
+    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);
+        installFilter(&canvas);
+        canvas.drawPicture(*pict);
+    }
+
+    static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
+#ifdef SK_SUPPORT_PDF
+        SkMatrix initialTransform = gm->getInitialTransform();
+        SkISize pageSize = gm->getISize();
+        SkPDFDevice* dev = NULL;
+        if (initialTransform.isIdentity()) {
+            dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
+        } else {
+            SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
+                                            SkIntToScalar(pageSize.height()));
+            initialTransform.mapRect(&content);
+            content.intersect(0, 0, SkIntToScalar(pageSize.width()),
+                              SkIntToScalar(pageSize.height()));
+            SkISize contentSize =
+                SkISize::Make(SkScalarRoundToInt(content.width()),
+                              SkScalarRoundToInt(content.height()));
+            dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
+        }
+        SkAutoUnref aur(dev);
+
+        SkCanvas c(dev);
+        invokeGM(gm, &c, true);
+
+        SkPDFDocument doc;
+        doc.appendPage(dev);
+        doc.emitPDF(&pdf);
+#endif
+    }
+
+    static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
+#ifdef SK_SUPPORT_XPS
+        SkISize size = gm->getISize();
+
+        SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
+                                       SkIntToScalar(size.height()));
+        static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
+        static const SkScalar upm = 72 * inchesPerMeter;
+        SkVector unitsPerMeter = SkPoint::Make(upm, upm);
+        static const SkScalar ppm = 200 * inchesPerMeter;
+        SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
+
+        SkXPSDevice* dev = new SkXPSDevice();
+        SkAutoUnref aur(dev);
+
+        SkCanvas c(dev);
+        dev->beginPortfolio(&xps);
+        dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
+        invokeGM(gm, &c);
+        dev->endSheet();
+        dev->endPortfolio();
+
+#endif
+    }
+
+    static ErrorBitfield write_reference_image(
+      const ConfigData& gRec, const char writePath [],
+      const char renderModeDescriptor [], const SkString& name,
+        SkBitmap& bitmap, SkDynamicMemoryWStream* document) {
+        SkString path;
+        bool success = false;
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend ||
+            (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
+
+            path = make_filename(writePath, renderModeDescriptor, name, "png");
+            success = write_bitmap(path, bitmap);
+        }
+        if (kPDF_Backend == gRec.fBackend) {
+            path = make_filename(writePath, renderModeDescriptor, name, "pdf");
+            success = write_document(path, *document);
+        }
+        if (kXPS_Backend == gRec.fBackend) {
+            path = make_filename(writePath, renderModeDescriptor, name, "xps");
+            success = write_document(path, *document);
+        }
+        if (success) {
+            return ERROR_NONE;
+        } else {
+            fprintf(stderr, "FAILED to write %s\n", path.c_str());
+            return ERROR_WRITING_REFERENCE_IMAGE;
+        }
+    }
+
+    // Compares bitmap "bitmap" to "referenceBitmap"; if they are
+    // different, writes out "bitmap" (in PNG format) within the
+    // diffPath subdir.
+    //
+    // Returns the ErrorBitfield from compare(), describing any differences
+    // between "bitmap" and "referenceBitmap" (or ERROR_NONE if there are none).
+    static ErrorBitfield compare_to_reference_image_in_memory(
+      const SkString& name, SkBitmap &bitmap, const SkBitmap& referenceBitmap,
+      const char diffPath [], const char renderModeDescriptor []) {
+        ErrorBitfield errors;
+        SkBitmap diffBitmap;
+        errors = compare(bitmap, referenceBitmap, name, renderModeDescriptor,
+                         diffPath ? &diffBitmap : NULL);
+        if ((ERROR_NONE != errors) && diffPath) {
+            // write out the generated image
+            SkString genName = make_filename(diffPath, "", name, "png");
+            if (!write_bitmap(genName, bitmap)) {
+                errors |= ERROR_WRITING_REFERENCE_IMAGE;
+            }
+        }
+        return errors;
+    }
+
+    // Compares bitmap "bitmap" to a reference bitmap read from disk;
+    // if they are different, writes out "bitmap" (in PNG format)
+    // within the diffPath subdir.
+    //
+    // Returns a description of the difference between "bitmap" and
+    // the reference bitmap, or ERROR_READING_REFERENCE_IMAGE if
+    // unable to read the reference bitmap from disk.
+    static ErrorBitfield compare_to_reference_image_on_disk(
+      const char readPath [], const SkString& name, SkBitmap &bitmap,
+      const char diffPath [], const char renderModeDescriptor []) {
+        SkString path = make_filename(readPath, "", name, "png");
+        SkBitmap referenceBitmap;
+        if (SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
+                                       SkBitmap::kARGB_8888_Config,
+                                       SkImageDecoder::kDecodePixels_Mode,
+                                       NULL)) {
+            return compare_to_reference_image_in_memory(name, bitmap,
+                                                        referenceBitmap,
+                                                        diffPath,
+                                                        renderModeDescriptor);
+        } else {
+            if (gNotifyMissingReadReference) {
+                fprintf(stderr, "FAILED to read %s\n", path.c_str());
+            }
+            return ERROR_READING_REFERENCE_IMAGE;
+        }
+    }
+
+    // NOTE: As far as I can tell, this function is NEVER called with a
+    // non-blank renderModeDescriptor, EXCEPT when readPath and writePath are
+    // both NULL (and thus no images are read from or written to disk).
+    // So I don't trust that the renderModeDescriptor is being used for
+    // anything other than debug output these days.
+    static ErrorBitfield handle_test_results(GM* gm,
+                                             const ConfigData& gRec,
+                                             const char writePath [],
+                                             const char readPath [],
+                                             const char diffPath [],
+                                             const char renderModeDescriptor [],
+                                             SkBitmap& bitmap,
+                                             SkDynamicMemoryWStream* pdf,
+                                             const SkBitmap* referenceBitmap) {
+        SkString name = make_name(gm->shortName(), gRec.fName);
+        ErrorBitfield retval = ERROR_NONE;
+
+        if (readPath && (gRec.fFlags & kRead_ConfigFlag)) {
+            retval |= compare_to_reference_image_on_disk(readPath, name, bitmap,
+                                                         diffPath,
+                                                         renderModeDescriptor);
+        }
+        if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
+            retval |= write_reference_image(gRec, writePath,
+                                            renderModeDescriptor,
+                                            name, bitmap, pdf);
+        }
+        if (referenceBitmap) {
+            retval |= compare_to_reference_image_in_memory(
+              name, bitmap, *referenceBitmap, diffPath, renderModeDescriptor);
+        }
+        return retval;
+    }
+
+    static SkPicture* generate_new_picture(GM* gm) {
+        // Pictures are refcounted so must be on heap
+        SkPicture* pict = new SkPicture;
+        SkISize size = gm->getISize();
+        SkCanvas* cv = pict->beginRecording(size.width(), size.height());
+        invokeGM(gm, 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 ErrorBitfield test_drawing(GM* gm,
+                                      const ConfigData& gRec,
+                                      const char writePath [],
+                                      const char readPath [],
+                                      const char diffPath [],
+                                      GrContext* context,
+                                      GrRenderTarget* rt,
+                                      SkBitmap* bitmap) {
+        SkDynamicMemoryWStream document;
+
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend) {
+            // Early exit if we can't generate the image.
+            ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
+                                                  false);
+            if (ERROR_NONE != errors) {
+                return errors;
+            }
+        } else if (gRec.fBackend == kPDF_Backend) {
+            generate_pdf(gm, document);
+#if CAN_IMAGE_PDF
+            SkAutoDataUnref data(document.copyToData());
+            SkMemoryStream stream(data->data(), data->size());
+            SkPDFDocumentToBitmap(&stream, bitmap);
+#endif
+        } else if (gRec.fBackend == kXPS_Backend) {
+            generate_xps(gm, document);
+        }
+        return handle_test_results(gm, gRec, writePath, readPath, diffPath,
+                                   "", *bitmap, &document, NULL);
+    }
+
+    static ErrorBitfield test_deferred_drawing(GM* gm,
+                                               const ConfigData& gRec,
+                                               const SkBitmap& referenceBitmap,
+                                               const char diffPath [],
+                                               GrContext* context,
+                                               GrRenderTarget* rt) {
+        SkDynamicMemoryWStream document;
+
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend) {
+            SkBitmap bitmap;
+            // Early exit if we can't generate the image, but this is
+            // expected in some cases, so don't report a test failure.
+            if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
+                return ERROR_NONE;
+            }
+            return handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                                       "-deferred", bitmap, NULL,
+                                       &referenceBitmap);
+        }
+        return ERROR_NONE;
+    }
+
+    static ErrorBitfield test_pipe_playback(GM* gm,
+                                            const ConfigData& gRec,
+                                            const SkBitmap& referenceBitmap,
+                                            const char readPath [],
+                                            const char diffPath []) {
+        ErrorBitfield errors = ERROR_NONE;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
+            SkBitmap bitmap;
+            SkISize size = gm->getISize();
+            setup_bitmap(gRec, size, &bitmap);
+            SkCanvas canvas(bitmap);
+            PipeController pipeController(&canvas);
+            SkGPipeWriter writer;
+            SkCanvas* pipeCanvas = writer.startRecording(
+              &pipeController, gPipeWritingFlagCombos[i].flags);
+            invokeGM(gm, pipeCanvas);
+            writer.endRecording();
+            SkString string("-pipe");
+            string.append(gPipeWritingFlagCombos[i].name);
+            errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                                          string.c_str(), bitmap, NULL,
+                                          &referenceBitmap);
+            if (errors != ERROR_NONE) {
+                break;
+            }
+        }
+        return errors;
+    }
+
+    static ErrorBitfield test_tiled_pipe_playback(
+      GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap,
+      const char readPath [], const char diffPath []) {
+        ErrorBitfield errors = ERROR_NONE;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
+            SkBitmap bitmap;
+            SkISize size = gm->getISize();
+            setup_bitmap(gRec, size, &bitmap);
+            SkCanvas canvas(bitmap);
+            TiledPipeController pipeController(bitmap);
+            SkGPipeWriter writer;
+            SkCanvas* pipeCanvas = writer.startRecording(
+              &pipeController, gPipeWritingFlagCombos[i].flags);
+            invokeGM(gm, pipeCanvas);
+            writer.endRecording();
+            SkString string("-tiled pipe");
+            string.append(gPipeWritingFlagCombos[i].name);
+            errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                                          string.c_str(), bitmap, NULL,
+                                          &referenceBitmap);
+            if (errors != ERROR_NONE) {
+                break;
+            }
+        }
+        return errors;
+    }
+}; // end of GMMain class definition
 
 #if SK_SUPPORT_GPU
 static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType;
@@ -847,6 +849,7 @@
     gSkSuppressFontCachePurgeSpew = true;
 
     setSystemPreferences();
+    GMMain gmmain;
 
     const char* writePath = NULL;   // if non-null, where we write the originals
     const char* writePicturePath = NULL;    // if non-null, where we write serialized pictures
@@ -928,8 +931,8 @@
         } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
             gNotifyMissingReadReference = true;
         } else if (strcmp(*argv, "--serialize") == 0) {
-            // Leaving in this option so that a user need not modify their command line arguments
-            // to still run.
+            // Leaving in this option so that a user need not modify
+            // their command line arguments to still run.
             doSerialize = true;
         } else if (strcmp(*argv, "--noserialize") == 0) {
             doSerialize = false;
@@ -1050,9 +1053,9 @@
             // Skip any tests that we don't even need to try.
             if ((kPDF_Backend == config.fBackend) &&
                 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
-            {
-                continue;
-            }
+                {
+                    continue;
+                }
             if ((gmFlags & GM::kSkip565_Flag) &&
                 (kRaster_Backend == config.fBackend) &&
                 (SkBitmap::kRGB_565_Config == config.fConfig)) {
@@ -1097,28 +1100,32 @@
             SkBitmap comparisonBitmap;
 
             if (ERROR_NONE == renderErrors) {
-                renderErrors |= test_drawing(gm, config,
-                                             writePath, readPath, diffPath,
-                                             GetGr(), renderTarget, &comparisonBitmap);
+                renderErrors |= gmmain.test_drawing(gm, config, writePath,
+                                                    readPath, diffPath, GetGr(),
+                                                    renderTarget,
+                                                    &comparisonBitmap);
             }
 
             if (doDeferred && !renderErrors &&
                 (kGPU_Backend == config.fBackend ||
                  kRaster_Backend == config.fBackend)) {
-                renderErrors |= test_deferred_drawing(gm, config, comparisonBitmap,
-                                                      diffPath, GetGr(), renderTarget);
+                renderErrors |= gmmain.test_deferred_drawing(gm, config,
+                                                             comparisonBitmap,
+                                                             diffPath, GetGr(),
+                                                             renderTarget);
             }
 
             testErrors |= renderErrors;
             if (is_recordable_failure(renderErrors)) {
-                failedTests.push_back(make_name(shortName, config.fName));
+                failedTests.push_back(gmmain.make_name(shortName,
+                                                       config.fName));
             }
         }
 
         SkBitmap comparisonBitmap;
         const ConfigData compareConfig =
             { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison" };
-        testErrors |= generate_image(gm, compareConfig, NULL, NULL, &comparisonBitmap, false);
+        testErrors |= gmmain.generate_image(gm, compareConfig, NULL, NULL, &comparisonBitmap, false);
 
         // run the picture centric GM steps
         if (!(gmFlags & GM::kSkipPicture_Flag)) {
@@ -1126,36 +1133,49 @@
             ErrorBitfield pictErrors = ERROR_NONE;
 
             //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
-            SkPicture* pict = generate_new_picture(gm);
+            SkPicture* pict = gmmain.generate_new_picture(gm);
             SkAutoUnref aur(pict);
 
             if ((ERROR_NONE == testErrors) && doReplay) {
                 SkBitmap bitmap;
-                generate_image_from_picture(gm, compareConfig, pict, &bitmap);
-                pictErrors |= handle_test_results(gm, compareConfig, NULL, NULL, diffPath,
-                                                  "-replay", bitmap, NULL, &comparisonBitmap);
+                gmmain.generate_image_from_picture(gm, compareConfig, pict,
+                                                   &bitmap);
+                pictErrors |= gmmain.handle_test_results(gm, compareConfig,
+                                                         NULL, NULL, diffPath,
+                                                         "-replay", bitmap,
+                                                         NULL,
+                                                         &comparisonBitmap);
                 if (is_recordable_failure(pictErrors)) {
-                    failedTests.push_back(make_name(shortName, "pict-replay"));
+                    failedTests.push_back(gmmain.make_name(shortName,
+                                                           "pict-replay"));
                 }
             }
 
-            if ((ERROR_NONE == testErrors) && (ERROR_NONE == pictErrors) && doSerialize) {
-                SkPicture* repict = stream_to_new_picture(*pict);
+            if ((ERROR_NONE == testErrors) &&
+                (ERROR_NONE == pictErrors) &&
+                doSerialize) {
+                SkPicture* repict = gmmain.stream_to_new_picture(*pict);
                 SkAutoUnref aurr(repict);
 
                 SkBitmap bitmap;
-                generate_image_from_picture(gm, compareConfig, repict, &bitmap);
-                pictErrors |= handle_test_results(gm, compareConfig, NULL, NULL, diffPath,
-                                                  "-serialize", bitmap, NULL, &comparisonBitmap);
+                gmmain.generate_image_from_picture(gm, compareConfig, repict,
+                                                   &bitmap);
+                pictErrors |= gmmain.handle_test_results(gm, compareConfig,
+                                                         NULL, NULL, diffPath,
+                                                         "-serialize", bitmap,
+                                                         NULL,
+                                                         &comparisonBitmap);
                 if (is_recordable_failure(pictErrors)) {
-                    failedTests.push_back(make_name(shortName, "pict-serialize"));
+                    failedTests.push_back(gmmain.make_name(shortName,
+                                                           "pict-serialize"));
                 }
             }
 
             if (writePicturePath) {
                 const char* pictureSuffix = "skp";
-                SkString path = make_filename(writePicturePath, "",
-                                              SkString(gm->shortName()), pictureSuffix);
+                SkString path = gmmain.make_filename(writePicturePath, "",
+                                                     SkString(gm->shortName()),
+                                                     pictureSuffix);
                 SkFILEWStream stream(path.c_str());
                 pict->serialize(&stream);
             }
@@ -1169,22 +1189,24 @@
             ErrorBitfield pipeErrors = ERROR_NONE;
 
             if ((ERROR_NONE == testErrors) && doPipe) {
-                pipeErrors |= test_pipe_playback(gm, compareConfig,
-                                                 comparisonBitmap,
-                                                 readPath, diffPath);
+                pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig,
+                                                        comparisonBitmap,
+                                                        readPath, diffPath);
                 if (is_recordable_failure(pipeErrors)) {
-                    failedTests.push_back(make_name(shortName, "pipe"));
+                    failedTests.push_back(gmmain.make_name(shortName, "pipe"));
                 }
             }
 
             if ((ERROR_NONE == testErrors) &&
                 (ERROR_NONE == pipeErrors) &&
                 doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) {
-                pipeErrors |= test_tiled_pipe_playback(gm, compareConfig,
-                                                       comparisonBitmap,
-                                                       readPath, diffPath);
+                pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig,
+                                                              comparisonBitmap,
+                                                              readPath,
+                                                              diffPath);
                 if (is_recordable_failure(pipeErrors)) {
-                    failedTests.push_back(make_name(shortName, "pipe-tiled"));
+                    failedTests.push_back(gmmain.make_name(shortName,
+                                                           "pipe-tiled"));
                 }
             }