Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "gm.h"
      9 #include "system_preferences.h"
     10 #include "GrContext.h"
     11 #include "GrRenderTarget.h"
     12 
     13 #include "SkColorPriv.h"
     14 #include "SkData.h"
     15 #include "SkDeferredCanvas.h"
     16 #include "SkDevice.h"
     17 #include "SkGpuCanvas.h"
     18 #include "SkGpuDevice.h"
     19 #include "SkGraphics.h"
     20 #include "SkImageDecoder.h"
     21 #include "SkImageEncoder.h"
     22 #include "gl/SkNativeGLContext.h"
     23 #include "gl/SkMesaGLContext.h"
     24 #include "SkPicture.h"
     25 #include "SkStream.h"
     26 #include "SkRefCnt.h"
     27 
     28 static bool gForceBWtext;
     29 
     30 extern bool gSkSuppressFontCachePurgeSpew;
     31 
     32 #ifdef SK_SUPPORT_PDF
     33     #include "SkPDFDevice.h"
     34     #include "SkPDFDocument.h"
     35 #endif
     36 
     37 // Until we resolve http://code.google.com/p/skia/issues/detail?id=455 ,
     38 // stop writing out XPS-format image baselines in gm.
     39 #undef SK_SUPPORT_XPS
     40 #ifdef SK_SUPPORT_XPS
     41     #include "SkXPSDevice.h"
     42 #endif
     43 
     44 #ifdef SK_BUILD_FOR_MAC
     45     #include "SkCGUtils.h"
     46     #define CAN_IMAGE_PDF   1
     47 #else
     48     #define CAN_IMAGE_PDF   0
     49 #endif
     50 
     51 typedef int ErrorBitfield;
     52 const static ErrorBitfield ERROR_NONE                    = 0x00;
     53 const static ErrorBitfield ERROR_NO_GPU_CONTEXT          = 0x01;
     54 const static ErrorBitfield ERROR_PIXEL_MISMATCH          = 0x02;
     55 const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04;
     56 const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08;
     57 const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10;
     58 
     59 using namespace skiagm;
     60 
     61 class Iter {
     62 public:
     63     Iter() {
     64         this->reset();
     65     }
     66 
     67     void reset() {
     68         fReg = GMRegistry::Head();
     69     }
     70 
     71     GM* next() {
     72         if (fReg) {
     73             GMRegistry::Factory fact = fReg->factory();
     74             fReg = fReg->next();
     75             return fact(0);
     76         }
     77         return NULL;
     78     }
     79 
     80     static int Count() {
     81         const GMRegistry* reg = GMRegistry::Head();
     82         int count = 0;
     83         while (reg) {
     84             count += 1;
     85             reg = reg->next();
     86         }
     87         return count;
     88     }
     89 
     90 private:
     91     const GMRegistry* fReg;
     92 };
     93 
     94 static SkString make_name(const char shortName[], const char configName[]) {
     95     SkString name(shortName);
     96     name.appendf("_%s", configName);
     97     return name;
     98 }
     99 
    100 static SkString make_filename(const char path[],
    101                               const char pathSuffix[],
    102                               const SkString& name,
    103                               const char suffix[]) {
    104     SkString filename(path);
    105     if (filename.endsWith("/")) {
    106         filename.remove(filename.size() - 1, 1);
    107     }
    108     filename.append(pathSuffix);
    109     filename.append("/");
    110     filename.appendf("%s.%s", name.c_str(), suffix);
    111     return filename;
    112 }
    113 
    114 /* since PNG insists on unpremultiplying our alpha, we take no precision chances
    115     and force all pixels to be 100% opaque, otherwise on compare we may not get
    116     a perfect match.
    117  */
    118 static void force_all_opaque(const SkBitmap& bitmap) {
    119     SkAutoLockPixels lock(bitmap);
    120     for (int y = 0; y < bitmap.height(); y++) {
    121         for (int x = 0; x < bitmap.width(); x++) {
    122             *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
    123         }
    124     }
    125 }
    126 
    127 static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
    128     SkBitmap copy;
    129     bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
    130     force_all_opaque(copy);
    131     return SkImageEncoder::EncodeFile(path.c_str(), copy,
    132                                       SkImageEncoder::kPNG_Type, 100);
    133 }
    134 
    135 static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
    136     int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
    137     int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
    138     int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
    139     return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
    140 }
    141 
    142 static void compute_diff(const SkBitmap& target, const SkBitmap& base,
    143                          SkBitmap* diff) {
    144     SkAutoLockPixels alp(*diff);
    145 
    146     const int w = target.width();
    147     const int h = target.height();
    148     for (int y = 0; y < h; y++) {
    149         for (int x = 0; x < w; x++) {
    150             SkPMColor c0 = *base.getAddr32(x, y);
    151             SkPMColor c1 = *target.getAddr32(x, y);
    152             SkPMColor d = 0;
    153             if (c0 != c1) {
    154                 d = compute_diff_pmcolor(c0, c1);
    155             }
    156             *diff->getAddr32(x, y) = d;
    157         }
    158     }
    159 }
    160 
    161 static ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base,
    162                              const SkString& name,
    163                              const char* renderModeDescriptor,
    164                              SkBitmap* diff) {
    165     SkBitmap copy;
    166     const SkBitmap* bm = &target;
    167     if (target.config() != SkBitmap::kARGB_8888_Config) {
    168         target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
    169         bm = &copy;
    170     }
    171     SkBitmap baseCopy;
    172     const SkBitmap* bp = &base;
    173     if (base.config() != SkBitmap::kARGB_8888_Config) {
    174         base.copyTo(&baseCopy, SkBitmap::kARGB_8888_Config);
    175         bp = &baseCopy;
    176     }
    177 
    178     force_all_opaque(*bm);
    179     force_all_opaque(*bp);
    180 
    181     const int w = bm->width();
    182     const int h = bm->height();
    183     if (w != bp->width() || h != bp->height()) {
    184         SkDebugf(
    185 "---- %s dimensions mismatch for %s base [%d %d] current [%d %d]\n",
    186                  renderModeDescriptor, name.c_str(),
    187                  bp->width(), bp->height(), w, h);
    188         return ERROR_DIMENSION_MISMATCH;
    189     }
    190 
    191     SkAutoLockPixels bmLock(*bm);
    192     SkAutoLockPixels baseLock(*bp);
    193 
    194     for (int y = 0; y < h; y++) {
    195         for (int x = 0; x < w; x++) {
    196             SkPMColor c0 = *bp->getAddr32(x, y);
    197             SkPMColor c1 = *bm->getAddr32(x, y);
    198             if (c0 != c1) {
    199                 SkDebugf(
    200 "----- %s pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
    201                          renderModeDescriptor, name.c_str(), x, y, c0, c1);
    202 
    203                 if (diff) {
    204                     diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
    205                     diff->allocPixels();
    206                     compute_diff(*bm, *bp, diff);
    207                 }
    208                 return ERROR_PIXEL_MISMATCH;
    209             }
    210         }
    211     }
    212 
    213     // they're equal
    214     return ERROR_NONE;
    215 }
    216 
    217 static bool write_document(const SkString& path,
    218                            const SkDynamicMemoryWStream& document) {
    219     SkFILEWStream stream(path.c_str());
    220     SkAutoDataUnref data(document.copyToData());
    221     return stream.writeData(data.get());
    222 }
    223 
    224 enum Backend {
    225   kRaster_Backend,
    226   kGPU_Backend,
    227   kPDF_Backend,
    228   kXPS_Backend,
    229 };
    230 
    231 struct ConfigData {
    232     SkBitmap::Config    fConfig;
    233     Backend             fBackend;
    234     const char*         fName;
    235 };
    236 
    237 /// Returns true if processing should continue, false to skip the
    238 /// remainder of this config for this GM.
    239 //@todo thudson 22 April 2011 - could refactor this to take in
    240 // a factory to generate the context, always call readPixels()
    241 // (logically a noop for rasters, if wasted time), and thus collapse the
    242 // GPU special case and also let this be used for SkPicture testing.
    243 static void setup_bitmap(const ConfigData& gRec, SkISize& size,
    244                          SkBitmap* bitmap) {
    245     bitmap->setConfig(gRec.fConfig, size.width(), size.height());
    246     bitmap->allocPixels();
    247     bitmap->eraseColor(0);
    248 }
    249 
    250 #include "SkDrawFilter.h"
    251 class BWTextDrawFilter : public SkDrawFilter {
    252 public:
    253     virtual void filter(SkPaint*, Type) SK_OVERRIDE;
    254 };
    255 void BWTextDrawFilter::filter(SkPaint* p, Type t) {
    256     if (kText_Type == t) {
    257         p->setAntiAlias(false);
    258     }
    259 }
    260 
    261 static void installFilter(SkCanvas* canvas) {
    262     if (gForceBWtext) {
    263         canvas->setDrawFilter(new BWTextDrawFilter)->unref();
    264     }
    265 }
    266 
    267 static void invokeGM(GM* gm, SkCanvas* canvas) {
    268     installFilter(canvas);
    269     gm->draw(canvas);
    270     canvas->setDrawFilter(NULL);
    271 }
    272 
    273 static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
    274                                     GrContext* context,
    275                                     GrRenderTarget* rt,
    276                                     SkBitmap* bitmap,
    277                                     bool deferred) {
    278     SkISize size (gm->getISize());
    279     setup_bitmap(gRec, size, bitmap);
    280 
    281     if (gRec.fBackend == kRaster_Backend) {
    282         SkCanvas* canvas;
    283         if (deferred) {
    284             canvas = new SkDeferredCanvas;
    285             canvas->setDevice(new SkDevice(*bitmap))->unref();
    286         } else {
    287             canvas = new SkCanvas(*bitmap);
    288         }
    289         SkAutoUnref canvasUnref(canvas);
    290         invokeGM(gm, canvas);
    291         canvas->flush();
    292     } else {  // GPU
    293         if (NULL == context) {
    294             return ERROR_NO_GPU_CONTEXT;
    295         }
    296         SkCanvas* gc;
    297         if (deferred) {
    298             gc = new SkDeferredCanvas;
    299         } else {
    300             gc = new SkGpuCanvas(context, rt);
    301         }
    302         SkAutoUnref gcUnref(gc);
    303         gc->setDevice(new SkGpuDevice(context, rt))->unref();
    304         invokeGM(gm, gc);
    305         // the device is as large as the current rendertarget, so we explicitly
    306         // only readback the amount we expect (in size)
    307         // overwrite our previous allocation
    308         bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
    309                                                        size.fHeight);
    310         gc->readPixels(bitmap, 0, 0);
    311     }
    312     return ERROR_NONE;
    313 }
    314 
    315 static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
    316                                         SkPicture* pict, SkBitmap* bitmap) {
    317     SkISize size = gm->getISize();
    318     setup_bitmap(gRec, size, bitmap);
    319     SkCanvas canvas(*bitmap);
    320     installFilter(&canvas);
    321     canvas.drawPicture(*pict);
    322 }
    323 
    324 static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
    325 #ifdef SK_SUPPORT_PDF
    326     SkISize size = gm->getISize();
    327     SkMatrix identity;
    328     identity.reset();
    329     SkPDFDevice* dev = new SkPDFDevice(size, size, identity);
    330     SkAutoUnref aur(dev);
    331 
    332     SkCanvas c(dev);
    333     invokeGM(gm, &c);
    334 
    335     SkPDFDocument doc;
    336     doc.appendPage(dev);
    337     doc.emitPDF(&pdf);
    338 #endif
    339 }
    340 
    341 static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
    342 #ifdef SK_SUPPORT_XPS
    343     SkISize size = gm->getISize();
    344 
    345     SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
    346                                    SkIntToScalar(size.height()));
    347     static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
    348     static const SkScalar upm = 72 * inchesPerMeter;
    349     SkVector unitsPerMeter = SkPoint::Make(upm, upm);
    350     static const SkScalar ppm = 200 * inchesPerMeter;
    351     SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
    352 
    353     SkXPSDevice* dev = new SkXPSDevice();
    354     SkAutoUnref aur(dev);
    355 
    356     SkCanvas c(dev);
    357     dev->beginPortfolio(&xps);
    358     dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
    359     invokeGM(gm, &c);
    360     dev->endSheet();
    361     dev->endPortfolio();
    362 
    363 #endif
    364 }
    365 
    366 static ErrorBitfield write_reference_image(const ConfigData& gRec,
    367                                            const char writePath [],
    368                                            const char renderModeDescriptor [],
    369                                            const SkString& name,
    370                                            SkBitmap& bitmap,
    371                                            SkDynamicMemoryWStream* document) {
    372     SkString path;
    373     bool success = false;
    374     if (gRec.fBackend == kRaster_Backend ||
    375         gRec.fBackend == kGPU_Backend ||
    376         (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
    377 
    378         path = make_filename(writePath, renderModeDescriptor, name, "png");
    379         success = write_bitmap(path, bitmap);
    380     }
    381     if (kPDF_Backend == gRec.fBackend) {
    382         path = make_filename(writePath, renderModeDescriptor, name, "pdf");
    383         success = write_document(path, *document);
    384     }
    385     if (kXPS_Backend == gRec.fBackend) {
    386         path = make_filename(writePath, renderModeDescriptor, name, "xps");
    387         success = write_document(path, *document);
    388     }
    389     if (success) {
    390         return ERROR_NONE;
    391     } else {
    392         fprintf(stderr, "FAILED to write %s\n", path.c_str());
    393         return ERROR_WRITING_REFERENCE_IMAGE;
    394     }
    395 }
    396 
    397 static ErrorBitfield compare_to_reference_image(const SkString& name,
    398                                                 SkBitmap &bitmap,
    399                                                 const SkBitmap& comparisonBitmap,
    400                                                 const char diffPath [],
    401                                                 const char renderModeDescriptor []) {
    402     ErrorBitfield errors;
    403     SkBitmap diffBitmap;
    404     errors = compare(bitmap, comparisonBitmap, name, renderModeDescriptor,
    405                      diffPath ? &diffBitmap : NULL);
    406     if ((ERROR_NONE == errors) && diffPath) {
    407         SkString diffName = make_filename(diffPath, "", name, ".diff.png");
    408         if (!write_bitmap(diffName, diffBitmap)) {
    409             errors |= ERROR_WRITING_REFERENCE_IMAGE;
    410         }
    411     }
    412     return errors;
    413 }
    414 
    415 static ErrorBitfield compare_to_reference_image(const char readPath [],
    416                                                 const SkString& name,
    417                                                 SkBitmap &bitmap,
    418                                                 const char diffPath [],
    419                                                 const char renderModeDescriptor []) {
    420     SkString path = make_filename(readPath, "", name, "png");
    421     SkBitmap orig;
    422     if (SkImageDecoder::DecodeFile(path.c_str(), &orig,
    423                                    SkBitmap::kARGB_8888_Config,
    424                                    SkImageDecoder::kDecodePixels_Mode, NULL)) {
    425         return compare_to_reference_image(name, bitmap,
    426                                           orig, diffPath,
    427                                           renderModeDescriptor);
    428     } else {
    429         fprintf(stderr, "FAILED to read %s\n", path.c_str());
    430         return ERROR_READING_REFERENCE_IMAGE;
    431     }
    432 }
    433 
    434 static ErrorBitfield handle_test_results(GM* gm,
    435                                          const ConfigData& gRec,
    436                                          const char writePath [],
    437                                          const char readPath [],
    438                                          const char diffPath [],
    439                                          const char renderModeDescriptor [],
    440                                          SkBitmap& bitmap,
    441                                          SkDynamicMemoryWStream* pdf,
    442                                          const SkBitmap* comparisonBitmap) {
    443     SkString name = make_name(gm->shortName(), gRec.fName);
    444 
    445     if (writePath) {
    446         return write_reference_image(gRec, writePath, renderModeDescriptor,
    447                                      name, bitmap, pdf);
    448     } else if (readPath && (
    449                    gRec.fBackend == kRaster_Backend ||
    450                    gRec.fBackend == kGPU_Backend ||
    451                    (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF))) {
    452         return compare_to_reference_image(readPath, name, bitmap,
    453                                    diffPath, renderModeDescriptor);
    454     } else if (comparisonBitmap) {
    455         return compare_to_reference_image(name, bitmap,
    456                                    *comparisonBitmap, diffPath,
    457                                    renderModeDescriptor);
    458     } else {
    459         return ERROR_NONE;
    460     }
    461 }
    462 
    463 static SkPicture* generate_new_picture(GM* gm) {
    464     // Pictures are refcounted so must be on heap
    465     SkPicture* pict = new SkPicture;
    466     SkCanvas* cv = pict->beginRecording(1000, 1000);
    467     invokeGM(gm, cv);
    468     pict->endRecording();
    469 
    470     return pict;
    471 }
    472 
    473 static SkPicture* stream_to_new_picture(const SkPicture& src) {
    474 
    475     // To do in-memory commiunications with a stream, we need to:
    476     // * create a dynamic memory stream
    477     // * copy it into a buffer
    478     // * create a read stream from it
    479     // ?!?!
    480 
    481     SkDynamicMemoryWStream storage;
    482     src.serialize(&storage);
    483 
    484     int streamSize = storage.getOffset();
    485     SkAutoMalloc dstStorage(streamSize);
    486     void* dst = dstStorage.get();
    487     //char* dst = new char [streamSize];
    488     //@todo thudson 22 April 2011 when can we safely delete [] dst?
    489     storage.copyTo(dst);
    490     SkMemoryStream pictReadback(dst, streamSize);
    491     SkPicture* retval = new SkPicture (&pictReadback);
    492     return retval;
    493 }
    494 
    495 // Test: draw into a bitmap or pdf.
    496 // Depending on flags, possibly compare to an expected image
    497 // and possibly output a diff image if it fails to match.
    498 static ErrorBitfield test_drawing(GM* gm,
    499                                   const ConfigData& gRec,
    500                                   const char writePath [],
    501                                   const char readPath [],
    502                                   const char diffPath [],
    503                                   GrContext* context,
    504                                   GrRenderTarget* rt,
    505                                   SkBitmap* bitmap) {
    506     SkDynamicMemoryWStream document;
    507 
    508     if (gRec.fBackend == kRaster_Backend ||
    509         gRec.fBackend == kGPU_Backend) {
    510         // Early exit if we can't generate the image.
    511         ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
    512             false);
    513         if (ERROR_NONE != errors) {
    514             return errors;
    515         }
    516     } else if (gRec.fBackend == kPDF_Backend) {
    517         generate_pdf(gm, document);
    518 #if CAN_IMAGE_PDF
    519         SkAutoDataUnref data(document.copyToData());
    520         SkMemoryStream stream(data.data(), data.size());
    521         SkPDFDocumentToBitmap(&stream, bitmap);
    522 #endif
    523     } else if (gRec.fBackend == kXPS_Backend) {
    524         generate_xps(gm, document);
    525     }
    526     return handle_test_results(gm, gRec, writePath, readPath, diffPath,
    527                                "", *bitmap, &document, NULL);
    528 }
    529 
    530 static ErrorBitfield test_deferred_drawing(GM* gm,
    531                          const ConfigData& gRec,
    532                          const SkBitmap& comparisonBitmap,
    533                          const char diffPath [],
    534                          GrContext* context,
    535                          GrRenderTarget* rt) {
    536     SkDynamicMemoryWStream document;
    537 
    538     if (gRec.fBackend == kRaster_Backend ||
    539         gRec.fBackend == kGPU_Backend) {
    540         SkBitmap bitmap;
    541         // Early exit if we can't generate the image, but this is
    542         // expected in some cases, so don't report a test failure.
    543         if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
    544             return ERROR_NONE;
    545         }
    546         return handle_test_results(gm, gRec, NULL, NULL, diffPath,
    547                                    "-deferred", bitmap, NULL, &comparisonBitmap);
    548     }
    549     return ERROR_NONE;
    550 }
    551 
    552 static ErrorBitfield test_picture_playback(GM* gm,
    553                                            const ConfigData& gRec,
    554                                            const SkBitmap& comparisonBitmap,
    555                                            const char readPath [],
    556                                            const char diffPath []) {
    557     SkPicture* pict = generate_new_picture(gm);
    558     SkAutoUnref aur(pict);
    559 
    560     if (kRaster_Backend == gRec.fBackend) {
    561         SkBitmap bitmap;
    562         generate_image_from_picture(gm, gRec, pict, &bitmap);
    563         return handle_test_results(gm, gRec, NULL, NULL, diffPath,
    564                             "-replay", bitmap, NULL, &comparisonBitmap);
    565     } else {
    566         return ERROR_NONE;
    567     }
    568 }
    569 
    570 static ErrorBitfield test_picture_serialization(GM* gm,
    571                                                 const ConfigData& gRec,
    572                                                 const SkBitmap& comparisonBitmap,
    573                                                 const char readPath [],
    574                                                 const char diffPath []) {
    575     SkPicture* pict = generate_new_picture(gm);
    576     SkAutoUnref aurp(pict);
    577     SkPicture* repict = stream_to_new_picture(*pict);
    578     SkAutoUnref aurr(repict);
    579 
    580     if (kRaster_Backend == gRec.fBackend) {
    581         SkBitmap bitmap;
    582         generate_image_from_picture(gm, gRec, repict, &bitmap);
    583         return handle_test_results(gm, gRec, NULL, NULL, diffPath,
    584                             "-serialize", bitmap, NULL, &comparisonBitmap);
    585     } else {
    586         return ERROR_NONE;
    587     }
    588 }
    589 
    590 static void usage(const char * argv0) {
    591     SkDebugf(
    592         "%s [-w writePath] [-r readPath] [-d diffPath] [--noreplay]\n"
    593         "    [--serialize] [--forceBWtext] [--nopdf] [--nodeferred]\n"
    594         "    [--match substring] [--notexturecache]"
    595 #if SK_MESA
    596         " [--mesagl]"
    597 #endif
    598         "\n\n", argv0);
    599     SkDebugf("    writePath: directory to write rendered images in.\n");
    600     SkDebugf(
    601 "    readPath: directory to read reference images from;\n"
    602 "        reports if any pixels mismatch between reference and new images\n");
    603     SkDebugf("    diffPath: directory to write difference images in.\n");
    604     SkDebugf("    --noreplay: do not exercise SkPicture replay.\n");
    605     SkDebugf(
    606 "    --serialize: exercise SkPicture serialization & deserialization.\n");
    607     SkDebugf("    --forceBWtext: disable text anti-aliasing.\n");
    608     SkDebugf("    --nopdf: skip the pdf rendering test pass.\n");
    609     SkDebugf("    --nodeferred: skip the deferred rendering test pass.\n");
    610     SkDebugf("    --match foo will only run tests that substring match foo.\n");
    611 #if SK_MESA
    612     SkDebugf("    --mesagl will run using the osmesa sw gl rasterizer.\n");
    613 #endif
    614     SkDebugf("    --notexturecache: disable the gpu texture cache.\n");
    615 }
    616 
    617 static const ConfigData gRec[] = {
    618     { SkBitmap::kARGB_8888_Config, kRaster_Backend, "8888" },
    619     { SkBitmap::kARGB_4444_Config, kRaster_Backend, "4444" },
    620     { SkBitmap::kRGB_565_Config,   kRaster_Backend, "565" },
    621 #ifdef SK_SCALAR_IS_FLOAT
    622     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    "gpu" },
    623 #endif
    624 #ifdef SK_SUPPORT_PDF
    625     { SkBitmap::kARGB_8888_Config, kPDF_Backend,    "pdf" },
    626 #endif
    627 #ifdef SK_SUPPORT_XPS
    628     { SkBitmap::kARGB_8888_Config, kXPS_Backend,    "xps" },
    629 #endif
    630 };
    631 
    632 static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
    633     if (0 == array.count()) {
    634         // no names, so don't skip anything
    635         return false;
    636     }
    637     for (int i = 0; i < array.count(); ++i) {
    638         if (strstr(name, array[i])) {
    639             // found the name, so don't skip
    640             return false;
    641         }
    642     }
    643     return true;
    644 }
    645 
    646 namespace skiagm {
    647 static GrContext* gGrContext;
    648 GrContext* GetGr() {
    649     return gGrContext;
    650 }
    651 }
    652 
    653 int main(int argc, char * const argv[]) {
    654     SkAutoGraphics ag;
    655     // we don't need to see this during a run
    656     gSkSuppressFontCachePurgeSpew = true;
    657 
    658     setSystemPreferences();
    659 
    660     const char* writePath = NULL;   // if non-null, where we write the originals
    661     const char* readPath = NULL;    // if non-null, were we read from to compare
    662     const char* diffPath = NULL;    // if non-null, where we write our diffs (from compare)
    663 
    664     SkTDArray<const char*> fMatches;
    665 
    666     bool doPDF = true;
    667     bool doReplay = true;
    668     bool doSerialize = false;
    669     bool useMesa = false;
    670     bool doDeferred = true;
    671     bool disableTextureCache = false;
    672 
    673     const char* const commandName = argv[0];
    674     char* const* stop = argv + argc;
    675     for (++argv; argv < stop; ++argv) {
    676         if (strcmp(*argv, "-w") == 0) {
    677             argv++;
    678             if (argv < stop && **argv) {
    679                 writePath = *argv;
    680             }
    681         } else if (strcmp(*argv, "-r") == 0) {
    682             argv++;
    683             if (argv < stop && **argv) {
    684                 readPath = *argv;
    685             }
    686         } else if (strcmp(*argv, "-d") == 0) {
    687             argv++;
    688             if (argv < stop && **argv) {
    689                 diffPath = *argv;
    690             }
    691         } else if (strcmp(*argv, "--forceBWtext") == 0) {
    692             gForceBWtext = true;
    693         } else if (strcmp(*argv, "--noreplay") == 0) {
    694             doReplay = false;
    695         } else if (strcmp(*argv, "--nopdf") == 0) {
    696             doPDF = false;
    697         } else if (strcmp(*argv, "--nodeferred") == 0) {
    698             doDeferred = false;
    699         } else if (strcmp(*argv, "--serialize") == 0) {
    700             doSerialize = true;
    701         } else if (strcmp(*argv, "--match") == 0) {
    702             ++argv;
    703             if (argv < stop && **argv) {
    704                 // just record the ptr, no need for a deep copy
    705                 *fMatches.append() = *argv;
    706             }
    707 #if SK_MESA
    708         } else if (strcmp(*argv, "--mesagl") == 0) {
    709             useMesa = true;
    710 #endif
    711         } else if (strcmp(*argv, "--notexturecache") == 0) {
    712           disableTextureCache = true;
    713         } else {
    714           usage(commandName);
    715           return -1;
    716         }
    717     }
    718     if (argv != stop) {
    719       usage(commandName);
    720       return -1;
    721     }
    722 
    723     int maxW = -1;
    724     int maxH = -1;
    725     Iter iter;
    726     GM* gm;
    727     while ((gm = iter.next()) != NULL) {
    728         SkISize size = gm->getISize();
    729         maxW = SkMax32(size.width(), maxW);
    730         maxH = SkMax32(size.height(), maxH);
    731     }
    732     // setup a GL context for drawing offscreen
    733     SkAutoTUnref<SkGLContext> glContext;
    734 #if SK_MESA
    735     if (useMesa) {
    736         glContext.reset(new SkMesaGLContext());
    737     } else
    738 #endif
    739     {
    740         glContext.reset(new SkNativeGLContext());
    741     }
    742 
    743     GrPlatformRenderTargetDesc rtDesc;
    744     if (glContext.get()->init(maxW, maxH)) {
    745         GrPlatform3DContext ctx =
    746             reinterpret_cast<GrPlatform3DContext>(glContext.get()->gl());
    747         gGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
    748         if (NULL != gGrContext) {
    749             rtDesc.fConfig = kSkia8888_PM_GrPixelConfig;
    750             rtDesc.fStencilBits = 8;
    751             rtDesc.fRenderTargetHandle = glContext.get()->getFBOID();
    752         }
    753     } else {
    754         fprintf(stderr, "could not create GL context.\n");
    755     }
    756 
    757     if (readPath) {
    758         fprintf(stderr, "reading from %s\n", readPath);
    759     } else if (writePath) {
    760         fprintf(stderr, "writing to %s\n", writePath);
    761     }
    762 
    763     // Accumulate success of all tests.
    764     int testsRun = 0;
    765     int testsPassed = 0;
    766     int testsFailed = 0;
    767     int testsMissingReferenceImages = 0;
    768 
    769     if (disableTextureCache) {
    770         skiagm::GetGr()->setTextureCacheLimits(0, 0);
    771     }
    772 
    773     iter.reset();
    774     while ((gm = iter.next()) != NULL) {
    775         const char* shortName = gm->shortName();
    776         if (skip_name(fMatches, shortName)) {
    777             SkDELETE(gm);
    778             continue;
    779         }
    780 
    781         SkISize size = gm->getISize();
    782         SkDebugf("drawing... %s [%d %d]\n", shortName,
    783                  size.width(), size.height());
    784         SkBitmap forwardRenderedBitmap;
    785 
    786         // Above we created an fbo for the context at maxW x maxH size.
    787         // Here we lie about the size of the rt. We claim it is the size
    788         // desired by the test. The reason is that rasterization may change
    789         // slightly when the viewport dimensions change. Previously, whenever
    790         // a new test was checked in that bumped maxW or maxH several images
    791         // would slightly change.
    792         rtDesc.fWidth = size.width();
    793         rtDesc.fHeight = size.height();
    794         SkAutoTUnref<GrRenderTarget> rt;
    795         if (gGrContext) {
    796             rt.reset(gGrContext->createPlatformRenderTarget(rtDesc));
    797         }
    798 
    799         for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
    800             // Skip any tests that we don't even need to try.
    801             uint32_t gmFlags = gm->getFlags();
    802             if ((kPDF_Backend == gRec[i].fBackend) &&
    803                 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
    804             {
    805                 continue;
    806             }
    807 
    808             // Now we know that we want to run this test and record its
    809             // success or failure.
    810             ErrorBitfield testErrors = ERROR_NONE;
    811 
    812             if ((ERROR_NONE == testErrors) &&
    813                 (kGPU_Backend == gRec[i].fBackend) &&
    814                 (NULL == rt.get())) {
    815                 fprintf(stderr, "Could not create render target for gpu.\n");
    816                 testErrors |= ERROR_NO_GPU_CONTEXT;
    817             }
    818 
    819             if (ERROR_NONE == testErrors) {
    820                 testErrors |= test_drawing(gm, gRec[i],
    821                                            writePath, readPath, diffPath,
    822                                            gGrContext,
    823                                            rt.get(), &forwardRenderedBitmap);
    824             }
    825 
    826             if (doDeferred && !testErrors &&
    827                 (kGPU_Backend == gRec[i].fBackend ||
    828                 kRaster_Backend == gRec[i].fBackend)) {
    829                 testErrors |= test_deferred_drawing(gm, gRec[i],
    830                                     forwardRenderedBitmap,
    831                                     diffPath, gGrContext, rt.get());
    832             }
    833 
    834             if ((ERROR_NONE == testErrors) && doReplay &&
    835                 !(gmFlags & GM::kSkipPicture_Flag)) {
    836                 testErrors |= test_picture_playback(gm, gRec[i],
    837                                                     forwardRenderedBitmap,
    838                                                     readPath, diffPath);
    839             }
    840 
    841             if ((ERROR_NONE == testErrors) && doSerialize) {
    842                 testErrors |= test_picture_serialization(gm, gRec[i],
    843                                                          forwardRenderedBitmap,
    844                                                          readPath, diffPath);
    845             }
    846 
    847             // Update overall results.
    848             // We only tabulate the particular error types that we currently
    849             // care about (e.g., missing reference images). Later on, if we
    850             // want to also tabulate pixel mismatches vs dimension mistmatches
    851             // (or whatever else), we can do so.
    852             testsRun++;
    853             if (ERROR_NONE == testErrors) {
    854                 testsPassed++;
    855             } else if (ERROR_READING_REFERENCE_IMAGE & testErrors) {
    856                 testsMissingReferenceImages++;
    857             } else {
    858                 testsFailed++;
    859             }
    860         }
    861         SkDELETE(gm);
    862     }
    863     printf("Ran %d tests: %d passed, %d failed, %d missing reference images\n",
    864            testsRun, testsPassed, testsFailed, testsMissingReferenceImages);
    865     return (0 == testsFailed) ? 0 : -1;
    866 }
    867