| /* | 
 |  * Copyright 2011 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "gm.h" | 
 | #include "system_preferences.h" | 
 | #include "SkColorPriv.h" | 
 | #include "SkData.h" | 
 | #include "SkDeferredCanvas.h" | 
 | #include "SkDevice.h" | 
 | #include "SkGPipe.h" | 
 | #include "SkGraphics.h" | 
 | #include "SkImageDecoder.h" | 
 | #include "SkImageEncoder.h" | 
 | #include "SkPicture.h" | 
 | #include "SkRefCnt.h" | 
 | #include "SkStream.h" | 
 | #include "SkTArray.h" | 
 | #include "SamplePipeControllers.h" | 
 |  | 
 | #if SK_SUPPORT_GPU | 
 | #include "GrContextFactory.h" | 
 | #include "GrRenderTarget.h" | 
 | #include "SkGpuDevice.h" | 
 | #include "SkGpuCanvas.h" | 
 | typedef GrContextFactory::GLContextType GLContextType; | 
 | #else | 
 | class GrContext; | 
 | class GrRenderTarget; | 
 | typedef int GLContextType; | 
 | #endif | 
 |  | 
 | static bool gForceBWtext; | 
 |  | 
 | extern bool gSkSuppressFontCachePurgeSpew; | 
 |  | 
 | #ifdef SK_SUPPORT_PDF | 
 |     #include "SkPDFDevice.h" | 
 |     #include "SkPDFDocument.h" | 
 | #endif | 
 |  | 
 | // Until we resolve http://code.google.com/p/skia/issues/detail?id=455 , | 
 | // stop writing out XPS-format image baselines in gm. | 
 | #undef SK_SUPPORT_XPS | 
 | #ifdef SK_SUPPORT_XPS | 
 |     #include "SkXPSDevice.h" | 
 | #endif | 
 |  | 
 | #ifdef SK_BUILD_FOR_MAC | 
 |     #include "SkCGUtils.h" | 
 |     #define CAN_IMAGE_PDF   1 | 
 | #else | 
 |     #define CAN_IMAGE_PDF   0 | 
 | #endif | 
 |  | 
 | typedef int ErrorBitfield; | 
 | const static ErrorBitfield ERROR_NONE                    = 0x00; | 
 | const static ErrorBitfield ERROR_NO_GPU_CONTEXT          = 0x01; | 
 | const static ErrorBitfield ERROR_PIXEL_MISMATCH          = 0x02; | 
 | const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04; | 
 | const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08; | 
 | const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10; | 
 |  | 
 | // If true, emit a messange when we can't find a reference image to compare | 
 | static bool gNotifyMissingReadReference; | 
 |  | 
 | using namespace skiagm; | 
 |  | 
 | class Iter { | 
 | public: | 
 |     Iter() { | 
 |         this->reset(); | 
 |     } | 
 |  | 
 |     void reset() { | 
 |         fReg = GMRegistry::Head(); | 
 |     } | 
 |  | 
 |     GM* next() { | 
 |         if (fReg) { | 
 |             GMRegistry::Factory fact = fReg->factory(); | 
 |             fReg = fReg->next(); | 
 |             return fact(0); | 
 |         } | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     static int Count() { | 
 |         const GMRegistry* reg = GMRegistry::Head(); | 
 |         int count = 0; | 
 |         while (reg) { | 
 |             count += 1; | 
 |             reg = reg->next(); | 
 |         } | 
 |         return count; | 
 |     } | 
 |  | 
 | private: | 
 |     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("/")) { | 
 |         filename.remove(filename.size() - 1, 1); | 
 |     } | 
 |     filename.append(pathSuffix); | 
 |     filename.append("/"); | 
 |     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(©, 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; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | static ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base, | 
 |                              const SkString& name, | 
 |                              const char* renderModeDescriptor, | 
 |                              SkBitmap* diff) { | 
 |     SkBitmap copy; | 
 |     const SkBitmap* bm = ⌖ | 
 |     if (target.config() != SkBitmap::kARGB_8888_Config) { | 
 |         target.copyTo(©, SkBitmap::kARGB_8888_Config); | 
 |         bm = © | 
 |     } | 
 |     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, | 
 |   kPDF_Backend, | 
 |   kXPS_Backend, | 
 | }; | 
 |  | 
 | enum ConfigFlags { | 
 |     kNone_ConfigFlag  = 0x0, | 
 |     /* Write GM images if a write path is provided. */ | 
 |     kWrite_ConfigFlag = 0x1, | 
 |     /* Read comparison GM images if a read path is provided. */ | 
 |     kRead_ConfigFlag  = 0x2, | 
 |     kRW_ConfigFlag    = (kWrite_ConfigFlag | kRead_ConfigFlag), | 
 | }; | 
 |  | 
 | struct ConfigData { | 
 |     SkBitmap::Config                fConfig; | 
 |     Backend                         fBackend; | 
 |     GLContextType                   fGLContextType; // GPU backend only | 
 |     int                             fSampleCnt;     // GPU backend only | 
 |     ConfigFlags                     fFlags; | 
 |     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; | 
 | }; | 
 | void BWTextDrawFilter::filter(SkPaint* p, Type t) { | 
 |     if (kText_Type == t) { | 
 |         p->setAntiAlias(false); | 
 |     } | 
 | } | 
 |  | 
 | 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); | 
 |  | 
 |     if (gRec.fBackend == kRaster_Backend) { | 
 |         SkCanvas* canvas; | 
 |         if (deferred) { | 
 |             canvas = new SkDeferredCanvas; | 
 |             canvas->setDevice(new SkDevice(*bitmap))->unref(); | 
 |         } else { | 
 |             canvas = new SkCanvas(*bitmap); | 
 |         } | 
 |         SkAutoUnref canvasUnref(canvas); | 
 |         invokeGM(gm, canvas); | 
 |         canvas->flush(); | 
 |     } | 
 | #if SK_SUPPORT_GPU | 
 |     else {  // GPU | 
 |         if (NULL == context) { | 
 |             return ERROR_NO_GPU_CONTEXT; | 
 |         } | 
 |         SkCanvas* gc; | 
 |         if (deferred) { | 
 |             gc = new SkDeferredCanvas; | 
 |         } else { | 
 |             gc = new SkGpuCanvas(context, rt); | 
 |         } | 
 |         SkAutoUnref gcUnref(gc); | 
 |         gc->setDevice(new SkGpuDevice(context, rt))->unref(); | 
 |         invokeGM(gm, gc); | 
 |         // 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); | 
 |         gc->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; | 
 |     } | 
 | } | 
 |  | 
 | static ErrorBitfield compare_to_reference_image(const SkString& name, | 
 |                                                 SkBitmap &bitmap, | 
 |                                                 const SkBitmap& comparisonBitmap, | 
 |                                                 const char diffPath [], | 
 |                                                 const char renderModeDescriptor []) { | 
 |     ErrorBitfield errors; | 
 |     SkBitmap diffBitmap; | 
 |     errors = compare(bitmap, comparisonBitmap, 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; | 
 | } | 
 |  | 
 | static ErrorBitfield compare_to_reference_image(const char readPath [], | 
 |                                                 const SkString& name, | 
 |                                                 SkBitmap &bitmap, | 
 |                                                 const char diffPath [], | 
 |                                                 const char renderModeDescriptor []) { | 
 |     SkString path = make_filename(readPath, "", name, "png"); | 
 |     SkBitmap orig; | 
 |     if (SkImageDecoder::DecodeFile(path.c_str(), &orig, | 
 |                                    SkBitmap::kARGB_8888_Config, | 
 |                                    SkImageDecoder::kDecodePixels_Mode, NULL)) { | 
 |         return compare_to_reference_image(name, bitmap, | 
 |                                           orig, diffPath, | 
 |                                           renderModeDescriptor); | 
 |     } else { | 
 |         if (gNotifyMissingReadReference) { | 
 |             fprintf(stderr, "FAILED to read %s\n", path.c_str()); | 
 |         } | 
 |         return ERROR_READING_REFERENCE_IMAGE; | 
 |     } | 
 | } | 
 |  | 
 | 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* comparisonBitmap) { | 
 |     SkString name = make_name(gm->shortName(), gRec.fName); | 
 |     ErrorBitfield retval = ERROR_NONE; | 
 |  | 
 |     if (readPath && (gRec.fFlags & kRead_ConfigFlag)) { | 
 |         retval |= compare_to_reference_image(readPath, name, bitmap, | 
 |                                              diffPath, renderModeDescriptor); | 
 |     } | 
 |     if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
 |         retval |= write_reference_image(gRec, writePath, renderModeDescriptor, | 
 |                                         name, bitmap, pdf); | 
 |     } | 
 |     if (comparisonBitmap) { | 
 |         retval |= compare_to_reference_image(name, bitmap, | 
 |                                              *comparisonBitmap, diffPath, | 
 |                                              renderModeDescriptor); | 
 |     } | 
 |     return retval; | 
 | } | 
 |  | 
 | 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); | 
 |     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& comparisonBitmap, | 
 |                          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, &comparisonBitmap); | 
 |     } | 
 |     return ERROR_NONE; | 
 | } | 
 |  | 
 | static ErrorBitfield test_picture_playback(GM* gm, | 
 |                                            const ConfigData& gRec, | 
 |                                            const SkBitmap& comparisonBitmap, | 
 |                                            const char readPath [], | 
 |                                            const char diffPath []) { | 
 |     SkPicture* pict = generate_new_picture(gm); | 
 |     SkAutoUnref aur(pict); | 
 |  | 
 |     if (kRaster_Backend == gRec.fBackend) { | 
 |         SkBitmap bitmap; | 
 |         generate_image_from_picture(gm, gRec, pict, &bitmap); | 
 |         return handle_test_results(gm, gRec, NULL, NULL, diffPath, | 
 |                             "-replay", bitmap, NULL, &comparisonBitmap); | 
 |     } else { | 
 |         return ERROR_NONE; | 
 |     } | 
 | } | 
 |  | 
 | static ErrorBitfield test_picture_serialization(GM* gm, | 
 |                                                 const ConfigData& gRec, | 
 |                                                 const SkBitmap& comparisonBitmap, | 
 |                                                 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); | 
 |  | 
 |     if (kRaster_Backend == gRec.fBackend) { | 
 |         SkBitmap bitmap; | 
 |         generate_image_from_picture(gm, gRec, repict, &bitmap); | 
 |         return handle_test_results(gm, gRec, NULL, NULL, diffPath, | 
 |                             "-serialize", bitmap, NULL, &comparisonBitmap); | 
 |     } else { | 
 |         return ERROR_NONE; | 
 |     } | 
 | } | 
 |  | 
 | struct PipeFlagComboData { | 
 |     const char* name; | 
 |     uint32_t flags; | 
 | }; | 
 |  | 
 | static PipeFlagComboData gPipeWritingFlagCombos[] = { | 
 |     { "", 0 }, | 
 |     { " cross-process", SkGPipeWriter::kCrossProcess_Flag }, | 
 |     { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag | 
 |         | SkGPipeWriter::kSharedAddressSpace_Flag } | 
 | }; | 
 |  | 
 | static ErrorBitfield test_pipe_playback(GM* gm, | 
 |                                         const ConfigData& gRec, | 
 |                                         const SkBitmap& comparisonBitmap, | 
 |                                         const char readPath [], | 
 |                                         const char diffPath []) { | 
 |     if (kRaster_Backend != gRec.fBackend) { | 
 |         return ERROR_NONE; | 
 |     } | 
 |     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, &comparisonBitmap); | 
 |         if (errors != ERROR_NONE) { | 
 |             break; | 
 |         } | 
 |     } | 
 |     return errors; | 
 | } | 
 |  | 
 | static ErrorBitfield test_tiled_pipe_playback(GM* gm, | 
 |                                         const ConfigData& gRec, | 
 |                                         const SkBitmap& comparisonBitmap, | 
 |                                         const char readPath [], | 
 |                                         const char diffPath []) { | 
 |     if (kRaster_Backend != gRec.fBackend) { | 
 |         return ERROR_NONE; | 
 |     } | 
 |     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, &comparisonBitmap); | 
 |         if (errors != ERROR_NONE) { | 
 |             break; | 
 |         } | 
 |     } | 
 |     return errors; | 
 | } | 
 |  | 
 | static void write_picture_serialization(GM* gm, const ConfigData& rec, | 
 |                                         const char writePicturePath[]) { | 
 |     // only do this once, so we pick raster | 
 |     if (kRaster_Backend == rec.fBackend && | 
 |         SkBitmap::kARGB_8888_Config == rec.fConfig) { | 
 |  | 
 |         SkAutoTUnref<SkPicture> pict(generate_new_picture(gm)); | 
 |  | 
 |         const char* pictureSuffix = "skp"; | 
 |         SkString path = make_filename(writePicturePath, "", | 
 |                                       SkString(gm->shortName()), pictureSuffix); | 
 |  | 
 |         SkFILEWStream stream(path.c_str()); | 
 |         pict->serialize(&stream); | 
 |     } | 
 | } | 
 |  | 
 | #if SK_SUPPORT_GPU | 
 | static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType; | 
 | #else | 
 | static const GLContextType kDontCare_GLContextType = 0; | 
 | #endif | 
 |  | 
 | // If the platform does not support writing PNGs of PDFs then there will be no | 
 | // comparison images to read. However, we can always write the .pdf files | 
 | static const ConfigFlags kPDFConfigFlags = CAN_IMAGE_PDF ? kRW_ConfigFlag : | 
 |                                                            kWrite_ConfigFlag; | 
 |  | 
 | static const ConfigData gRec[] = { | 
 |     { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "8888" }, | 
 |     { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "4444" }, | 
 |     { SkBitmap::kRGB_565_Config,   kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "565" }, | 
 | #if defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType,  0, kRW_ConfigFlag,    "gpu" }, | 
 | #ifndef SK_BUILD_FOR_ANDROID | 
 |     // currently we don't want to run MSAA tests on Android | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag,    "msaa16" }, | 
 | #endif | 
 |     /* The debug context does not generate images */ | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kDebug_GLContextType,   0, kNone_ConfigFlag,  "debug" }, | 
 | #if SK_ANGLE | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,   0, kRW_ConfigFlag,    "angle" }, | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,  16, kRW_ConfigFlag,    "anglemsaa16" }, | 
 | #endif // SK_ANGLE | 
 | #ifdef SK_MESA | 
 |     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kMESA_GLContextType,    0, kRW_ConfigFlag,    "mesa" }, | 
 | #endif // SK_MESA | 
 | #endif // defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU | 
 | #ifdef SK_SUPPORT_XPS | 
 |     /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */ | 
 |     { SkBitmap::kARGB_8888_Config, kXPS_Backend,    kDontCare_GLContextType,                  0, kWrite_ConfigFlag, "xps" }, | 
 | #endif // SK_SUPPORT_XPS | 
 | #ifdef SK_SUPPORT_PDF | 
 |     { SkBitmap::kARGB_8888_Config, kPDF_Backend,    kDontCare_GLContextType,                  0, kPDFConfigFlags,   "pdf" }, | 
 | #endif // SK_SUPPORT_PDF | 
 | }; | 
 |  | 
 | static void usage(const char * argv0) { | 
 |     SkDebugf("%s\n", argv0); | 
 |     SkDebugf("    [-w writePath] [-r readPath] [-d diffPath] [-i resourcePath]\n"); | 
 |     SkDebugf("    [-wp writePicturePath]\n"); | 
 |     SkDebugf("    [--config "); | 
 |     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { | 
 |         if (i > 0) { | 
 |             SkDebugf("|"); | 
 |         } | 
 |         SkDebugf(gRec[i].fName); | 
 |     } | 
 |     SkDebugf(" ]\n"); | 
 |     SkDebugf("    [--noreplay] [--nopipe] [--serialize] [--forceBWtext] [--nopdf] \n" | 
 |              "    [--tiledPipe] \n" | 
 |              "    [--nodeferred] [--match substring] [--notexturecache]\n" | 
 |              "    [-h|--help]\n" | 
 |              ); | 
 |     SkDebugf("    writePath: directory to write rendered images in.\n"); | 
 |     SkDebugf("    writePicturePath: directory to write images to in .skp format.\n"); | 
 |     SkDebugf( | 
 |              "    readPath: directory to read reference images from;\n" | 
 |              "        reports if any pixels mismatch between reference and new images\n"); | 
 |     SkDebugf("    diffPath: directory to write difference images in.\n"); | 
 |     SkDebugf("    resourcePath: directory that stores image resources.\n"); | 
 |     SkDebugf("    --noreplay: do not exercise SkPicture replay.\n"); | 
 |     SkDebugf("    --nopipe: Skip SkGPipe replay.\n"); | 
 |     SkDebugf("    --tiledPipe: Exercise tiled SkGPipe replay.\n"); | 
 |     SkDebugf( | 
 |              "    --serialize: exercise SkPicture serialization & deserialization.\n"); | 
 |     SkDebugf("    --forceBWtext: disable text anti-aliasing.\n"); | 
 |     SkDebugf("    --nopdf: skip the pdf rendering test pass.\n"); | 
 |     SkDebugf("    --nodeferred: skip the deferred rendering test pass.\n"); | 
 |     SkDebugf("    --match foo: will only run tests that substring match foo.\n"); | 
 |     SkDebugf("    --notexturecache: disable the gpu texture cache.\n"); | 
 |     SkDebugf("    -h|--help : Show this help message. \n"); | 
 | } | 
 |  | 
 | static int findConfig(const char config[]) { | 
 |     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { | 
 |         if (!strcmp(config, gRec[i].fName)) { | 
 |             return i; | 
 |         } | 
 |     } | 
 |     return -1; | 
 | } | 
 |  | 
 | static bool skip_name(const SkTDArray<const char*> array, const char name[]) { | 
 |     if (0 == array.count()) { | 
 |         // no names, so don't skip anything | 
 |         return false; | 
 |     } | 
 |     for (int i = 0; i < array.count(); ++i) { | 
 |         if (strstr(name, array[i])) { | 
 |             // found the name, so don't skip | 
 |             return false; | 
 |         } | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | namespace skiagm { | 
 | #if SK_SUPPORT_GPU | 
 | SkAutoTUnref<GrContext> gGrContext; | 
 | /** | 
 |  * Sets the global GrContext, accessible by indivual GMs | 
 |  */ | 
 | static void SetGr(GrContext* grContext) { | 
 |     SkSafeRef(grContext); | 
 |     gGrContext.reset(grContext); | 
 | } | 
 |  | 
 | /** | 
 |  * Gets the global GrContext, can be called by GM tests. | 
 |  */ | 
 | GrContext* GetGr(); | 
 | GrContext* GetGr() { | 
 |     return gGrContext.get(); | 
 | } | 
 |  | 
 | /** | 
 |  * Sets the global GrContext and then resets it to its previous value at | 
 |  * destruction. | 
 |  */ | 
 | class AutoResetGr : SkNoncopyable { | 
 | public: | 
 |     AutoResetGr() : fOld(NULL) {} | 
 |     void set(GrContext* context) { | 
 |         SkASSERT(NULL == fOld); | 
 |         fOld = GetGr(); | 
 |         SkSafeRef(fOld); | 
 |         SetGr(context); | 
 |     } | 
 |     ~AutoResetGr() { SetGr(fOld); SkSafeUnref(fOld); } | 
 | private: | 
 |     GrContext* fOld; | 
 | }; | 
 | #else | 
 | GrContext* GetGr() { return NULL; } | 
 | #endif | 
 | } | 
 |  | 
 | int main(int argc, char * const argv[]) { | 
 |  | 
 | #ifdef SK_ENABLE_INST_COUNT | 
 |     gPrintInstCount = true; | 
 | #endif | 
 |  | 
 |     SkGraphics::Init(); | 
 |     // we don't need to see this during a run | 
 |     gSkSuppressFontCachePurgeSpew = true; | 
 |  | 
 |     setSystemPreferences(); | 
 |  | 
 |     const char* writePath = NULL;   // if non-null, where we write the originals | 
 |     const char* writePicturePath = NULL;    // if non-null, where we write serialized pictures | 
 |     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* resourcePath = NULL;// if non-null, where we read from for image resources | 
 |  | 
 |     SkTDArray<const char*> fMatches; | 
 |  | 
 |     bool doPDF = true; | 
 |     bool doReplay = true; | 
 |     bool doPipe = true; | 
 |     bool doTiledPipe = false; | 
 |     bool doSerialize = false; | 
 |     bool doDeferred = true; | 
 |     bool disableTextureCache = false; | 
 |     SkTDArray<size_t> configs; | 
 |     bool userConfig = false; | 
 |  | 
 |     gNotifyMissingReadReference = true; | 
 |  | 
 |     const char* const commandName = argv[0]; | 
 |     char* const* stop = argv + argc; | 
 |     for (++argv; argv < stop; ++argv) { | 
 |         if (strcmp(*argv, "-w") == 0) { | 
 |             argv++; | 
 |             if (argv < stop && **argv) { | 
 |                 writePath = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "-wp") == 0) { | 
 |             argv++; | 
 |             if (argv < stop && **argv) { | 
 |                 writePicturePath = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "-r") == 0) { | 
 |             argv++; | 
 |             if (argv < stop && **argv) { | 
 |                 readPath = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "-d") == 0) { | 
 |             argv++; | 
 |             if (argv < stop && **argv) { | 
 |                 diffPath = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "-i") == 0) { | 
 |             argv++; | 
 |             if (argv < stop && **argv) { | 
 |                 resourcePath = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "--forceBWtext") == 0) { | 
 |             gForceBWtext = true; | 
 |         } else if (strcmp(*argv, "--nopipe") == 0) { | 
 |             doPipe = false; | 
 |         } else if (strcmp(*argv, "--tiledPipe") == 0) { | 
 |             doTiledPipe = true; | 
 |         } else if (strcmp(*argv, "--noreplay") == 0) { | 
 |             doReplay = false; | 
 |         } else if (strcmp(*argv, "--nopdf") == 0) { | 
 |             doPDF = false; | 
 |         } else if (strcmp(*argv, "--nodeferred") == 0) { | 
 |             doDeferred = false; | 
 |         } else if (strcmp(*argv, "--disable-missing-warning") == 0) { | 
 |             gNotifyMissingReadReference = false; | 
 |         } else if (strcmp(*argv, "--enable-missing-warning") == 0) { | 
 |             gNotifyMissingReadReference = true; | 
 |         } else if (strcmp(*argv, "--serialize") == 0) { | 
 |             doSerialize = true; | 
 |         } else if (strcmp(*argv, "--match") == 0) { | 
 |             ++argv; | 
 |             if (argv < stop && **argv) { | 
 |                 // just record the ptr, no need for a deep copy | 
 |                 *fMatches.append() = *argv; | 
 |             } | 
 |         } else if (strcmp(*argv, "--notexturecache") == 0) { | 
 |             disableTextureCache = true; | 
 |         } else if (strcmp(*argv, "--config") == 0) { | 
 |             argv++; | 
 |             if (argv < stop) { | 
 |                 int index = findConfig(*argv); | 
 |                 if (index >= 0) { | 
 |                     *configs.append() = index; | 
 |                     userConfig = true; | 
 |                 } else { | 
 |                     SkString str; | 
 |                     str.printf("unrecognized config %s\n", *argv); | 
 |                     SkDebugf(str.c_str()); | 
 |                     usage(commandName); | 
 |                     return -1; | 
 |                 } | 
 |             } else { | 
 |                 SkDebugf("missing arg for --config\n"); | 
 |                 usage(commandName); | 
 |                 return -1; | 
 |             } | 
 |         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { | 
 |             usage(commandName); | 
 |             return -1; | 
 |         } else { | 
 |             usage(commandName); | 
 |             return -1; | 
 |         } | 
 |     } | 
 |     if (argv != stop) { | 
 |         usage(commandName); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     if (!userConfig) { | 
 |         // if no config is specified by user, we add them all. | 
 |         for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { | 
 |             *configs.append() = i; | 
 |         } | 
 |     } | 
 |  | 
 |     GM::SetResourcePath(resourcePath); | 
 |  | 
 |     if (readPath) { | 
 |         fprintf(stderr, "reading from %s\n", readPath); | 
 |     } | 
 |     if (writePath) { | 
 |         fprintf(stderr, "writing to %s\n", writePath); | 
 |     } | 
 |     if (writePicturePath) { | 
 |         fprintf(stderr, "writing pictures to %s\n", writePicturePath); | 
 |     } | 
 |     if (resourcePath) { | 
 |         fprintf(stderr, "reading resources from %s\n", resourcePath); | 
 |     } | 
 |  | 
 |     // Accumulate success of all tests. | 
 |     int testsRun = 0; | 
 |     int testsPassed = 0; | 
 |     int testsFailed = 0; | 
 |     int testsMissingReferenceImages = 0; | 
 |  | 
 | #if SK_SUPPORT_GPU | 
 |     GrContextFactory* grFactory = new GrContextFactory; | 
 |     if (disableTextureCache) { | 
 |         skiagm::GetGr()->setTextureCacheLimits(0, 0); | 
 |     } | 
 | #endif | 
 |  | 
 |     SkTArray<SkString> failedTests; | 
 |  | 
 |     Iter iter; | 
 |     GM* gm; | 
 |     while ((gm = iter.next()) != NULL) { | 
 |         const char* shortName = gm->shortName(); | 
 |         if (skip_name(fMatches, shortName)) { | 
 |             SkDELETE(gm); | 
 |             continue; | 
 |         } | 
 |  | 
 |         SkISize size = gm->getISize(); | 
 |         SkDebugf("drawing... %s [%d %d]\n", shortName, | 
 |                  size.width(), size.height()); | 
 |         SkBitmap forwardRenderedBitmap; | 
 |  | 
 |         for (int i = 0; i < configs.count(); i++) { | 
 |             ConfigData config = gRec[configs[i]]; | 
 |             // Skip any tests that we don't even need to try. | 
 |             uint32_t gmFlags = gm->getFlags(); | 
 |             if ((kPDF_Backend == config.fBackend) && | 
 |                 (!doPDF || (gmFlags & GM::kSkipPDF_Flag))) | 
 |             { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Now we know that we want to run this test and record its | 
 |             // success or failure. | 
 |             ErrorBitfield testErrors = ERROR_NONE; | 
 |             GrRenderTarget* renderTarget = NULL; | 
 | #if SK_SUPPORT_GPU | 
 |             SkAutoTUnref<GrRenderTarget> rt; | 
 |             AutoResetGr autogr; | 
 |             if ((ERROR_NONE == testErrors) && | 
 |                 kGPU_Backend == config.fBackend) { | 
 |                 GrContext* gr = grFactory->get(config.fGLContextType); | 
 |                 bool grSuccess = false; | 
 |                 if (gr) { | 
 |                     // create a render target to back the device | 
 |                     GrTextureDesc desc; | 
 |                     desc.fConfig = kSkia8888_PM_GrPixelConfig; | 
 |                     desc.fFlags = kRenderTarget_GrTextureFlagBit; | 
 |                     desc.fWidth = gm->getISize().width(); | 
 |                     desc.fHeight = gm->getISize().height(); | 
 |                     desc.fSampleCnt = config.fSampleCnt; | 
 |                     GrTexture* tex = gr->createUncachedTexture(desc, NULL, 0); | 
 |                     if (tex) { | 
 |                         rt.reset(tex->asRenderTarget()); | 
 |                         rt.get()->ref(); | 
 |                         tex->unref(); | 
 |                         autogr.set(gr); | 
 |                         renderTarget = rt.get(); | 
 |                         grSuccess = NULL != renderTarget; | 
 |                     } | 
 |                 } | 
 |                 if (!grSuccess) { | 
 |                     testErrors |= ERROR_NO_GPU_CONTEXT; | 
 |                 } | 
 |             } | 
 | #endif | 
 |  | 
 |             if (ERROR_NONE == testErrors) { | 
 |                 testErrors |= test_drawing(gm, config, | 
 |                                            writePath, readPath, diffPath, | 
 |                                            GetGr(), | 
 |                                            renderTarget, &forwardRenderedBitmap); | 
 |             } | 
 |  | 
 |             if (doDeferred && !testErrors && | 
 |                 (kGPU_Backend == config.fBackend || | 
 |                  kRaster_Backend == config.fBackend)) { | 
 |                 testErrors |= test_deferred_drawing(gm, config, | 
 |                                                     forwardRenderedBitmap, | 
 |                                                     diffPath, GetGr(), renderTarget); | 
 |             } | 
 |  | 
 |             if ((ERROR_NONE == testErrors) && doReplay && | 
 |                 !(gmFlags & GM::kSkipPicture_Flag)) { | 
 |                 testErrors |= test_picture_playback(gm, config, | 
 |                                                     forwardRenderedBitmap, | 
 |                                                     readPath, diffPath); | 
 |             } | 
 |  | 
 |             if ((ERROR_NONE == testErrors) && doPipe && | 
 |                 !(gmFlags & GM::kSkipPipe_Flag)) { | 
 |                 testErrors |= test_pipe_playback(gm, config, | 
 |                                                  forwardRenderedBitmap, | 
 |                                                  readPath, diffPath); | 
 |             } | 
 |  | 
 |             if ((ERROR_NONE == testErrors) && doTiledPipe && | 
 |                 !(gmFlags & GM::kSkipPipe_Flag)) { | 
 |                 testErrors |= test_tiled_pipe_playback(gm, config, | 
 |                                                  forwardRenderedBitmap, | 
 |                                                  readPath, diffPath); | 
 |             } | 
 |  | 
 |             if ((ERROR_NONE == testErrors) && doSerialize  && | 
 |                 !(gmFlags & GM::kSkipPicture_Flag)) { | 
 |                 testErrors |= test_picture_serialization(gm, config, | 
 |                                                          forwardRenderedBitmap, | 
 |                                                          readPath, diffPath); | 
 |             } | 
 |  | 
 |             if (!(gmFlags & GM::kSkipPicture_Flag) && writePicturePath) { | 
 |                 write_picture_serialization(gm, config, writePicturePath); | 
 |             } | 
 |  | 
 |             // Update overall results. | 
 |             // We only tabulate the particular error types that we currently | 
 |             // care about (e.g., missing reference images). Later on, if we | 
 |             // want to also tabulate pixel mismatches vs dimension mistmatches | 
 |             // (or whatever else), we can do so. | 
 |             testsRun++; | 
 |             if (ERROR_NONE == testErrors) { | 
 |                 testsPassed++; | 
 |             } else if (ERROR_READING_REFERENCE_IMAGE & testErrors) { | 
 |                 testsMissingReferenceImages++; | 
 |             } else { | 
 |                 testsFailed++; | 
 |  | 
 |                 failedTests.push_back(make_name(shortName, config.fName)); | 
 |             } | 
 |         } | 
 |         SkDELETE(gm); | 
 |     } | 
 |     SkDebugf("Ran %d tests: %d passed, %d failed, %d missing reference images\n", | 
 |              testsRun, testsPassed, testsFailed, testsMissingReferenceImages); | 
 |     for (int i = 0; i < failedTests.count(); ++i) { | 
 |         SkDebugf("\t\t%s\n", failedTests[i].c_str()); | 
 |     } | 
 | #if SK_SUPPORT_GPU | 
 |     delete grFactory; | 
 | #endif | 
 |     SkGraphics::Term(); | 
 |  | 
 |     return (0 == testsFailed) ? 0 : -1; | 
 | } |