gmmain.cpp revision 65caeaf32d09f5886f3c740cfef2f1c26ef9cb50
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/*
9 * Code for the "gm" (Golden Master) rendering comparison tool.
10 *
11 * If you make changes to this, re-run the self-tests at gm/tests/run.sh
12 * to make sure they still pass... you may need to change the expected
13 * results of the self-test.
14 */
15
16#include "gm.h"
17#include "gm_error.h"
18#include "gm_expectations.h"
19#include "system_preferences.h"
20#include "SkBitmap.h"
21#include "SkColorPriv.h"
22#include "SkCommandLineFlags.h"
23#include "SkData.h"
24#include "SkDeferredCanvas.h"
25#include "SkDevice.h"
26#include "SkDrawFilter.h"
27#include "SkForceLinking.h"
28#include "SkGPipe.h"
29#include "SkGraphics.h"
30#include "SkImageDecoder.h"
31#include "SkImageEncoder.h"
32#include "SkOSFile.h"
33#include "SkPDFRasterizer.h"
34#include "SkPicture.h"
35#include "SkRefCnt.h"
36#include "SkStream.h"
37#include "SkTArray.h"
38#include "SkTDict.h"
39#include "SkTileGridPicture.h"
40#include "SamplePipeControllers.h"
41
42#ifdef SK_DEBUG
43static const bool kDebugOnly = true;
44#else
45static const bool kDebugOnly = false;
46#endif
47
48__SK_FORCE_IMAGE_DECODER_LINKING;
49
50#ifdef SK_BUILD_FOR_WIN
51    // json includes xlocale which generates warning 4530 because we're compiling without
52    // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
53    #pragma warning(push)
54    #pragma warning(disable : 4530)
55#endif
56#include "json/value.h"
57#ifdef SK_BUILD_FOR_WIN
58    #pragma warning(pop)
59#endif
60
61#if SK_SUPPORT_GPU
62#include "GrContextFactory.h"
63#include "SkGpuDevice.h"
64typedef GrContextFactory::GLContextType GLContextType;
65#define DEFAULT_CACHE_VALUE -1
66static int gGpuCacheSizeBytes;
67static int gGpuCacheSizeCount;
68#else
69class GrContextFactory;
70class GrContext;
71class GrSurface;
72typedef int GLContextType;
73#endif
74
75#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
76
77extern bool gSkSuppressFontCachePurgeSpew;
78
79#ifdef SK_SUPPORT_PDF
80    #include "SkPDFDevice.h"
81    #include "SkPDFDocument.h"
82#endif
83
84// Until we resolve http://code.google.com/p/skia/issues/detail?id=455 ,
85// stop writing out XPS-format image baselines in gm.
86#undef SK_SUPPORT_XPS
87#ifdef SK_SUPPORT_XPS
88    #include "SkXPSDevice.h"
89#endif
90
91#ifdef SK_BUILD_FOR_MAC
92    #include "SkCGUtils.h"
93#endif
94
95using namespace skiagm;
96
97class Iter {
98public:
99    Iter() {
100        this->reset();
101    }
102
103    void reset() {
104        fReg = GMRegistry::Head();
105    }
106
107    GM* next() {
108        if (fReg) {
109            GMRegistry::Factory fact = fReg->factory();
110            fReg = fReg->next();
111            return fact(0);
112        }
113        return NULL;
114    }
115
116    static int Count() {
117        const GMRegistry* reg = GMRegistry::Head();
118        int count = 0;
119        while (reg) {
120            count += 1;
121            reg = reg->next();
122        }
123        return count;
124    }
125
126private:
127    const GMRegistry* fReg;
128};
129
130// TODO(epoger): Right now, various places in this code assume that all the
131// image files read/written by GM use this file extension.
132// Search for references to this constant to find these assumptions.
133const static char kPNG_FileExtension[] = "png";
134
135enum Backend {
136    kRaster_Backend,
137    kGPU_Backend,
138    kPDF_Backend,
139    kXPS_Backend,
140};
141
142enum BbhType {
143    kNone_BbhType,
144    kRTree_BbhType,
145    kTileGrid_BbhType,
146};
147
148enum ConfigFlags {
149    kNone_ConfigFlag  = 0x0,
150    /* Write GM images if a write path is provided. */
151    kWrite_ConfigFlag = 0x1,
152    /* Read reference GM images if a read path is provided. */
153    kRead_ConfigFlag  = 0x2,
154    kRW_ConfigFlag    = (kWrite_ConfigFlag | kRead_ConfigFlag),
155};
156
157struct ConfigData {
158    SkBitmap::Config                fConfig;
159    Backend                         fBackend;
160    GLContextType                   fGLContextType; // GPU backend only
161    int                             fSampleCnt;     // GPU backend only
162    ConfigFlags                     fFlags;
163    const char*                     fName;
164    bool                            fRunByDefault;
165};
166
167struct PDFRasterizerData {
168    bool        (*fRasterizerFunction)(SkStream*, SkBitmap*);
169    const char* fName;
170    bool        fRunByDefault;
171};
172
173class BWTextDrawFilter : public SkDrawFilter {
174public:
175    virtual bool filter(SkPaint*, Type) SK_OVERRIDE;
176};
177bool BWTextDrawFilter::filter(SkPaint* p, Type t) {
178    if (kText_Type == t) {
179        p->setAntiAlias(false);
180    }
181    return true;
182}
183
184struct PipeFlagComboData {
185    const char* name;
186    uint32_t flags;
187};
188
189static PipeFlagComboData gPipeWritingFlagCombos[] = {
190    { "", 0 },
191    { " cross-process", SkGPipeWriter::kCrossProcess_Flag },
192    { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag
193        | SkGPipeWriter::kSharedAddressSpace_Flag }
194};
195
196static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect);
197
198const static ErrorCombination kDefaultIgnorableErrorTypes = ErrorCombination()
199    .plus(kMissingExpectations_ErrorType)
200    .plus(kIntentionallySkipped_ErrorType);
201
202class GMMain {
203public:
204    GMMain() : fUseFileHierarchy(false), fWriteChecksumBasedFilenames(false),
205               fIgnorableErrorTypes(kDefaultIgnorableErrorTypes),
206               fMismatchPath(NULL), fMissingExpectationsPath(NULL), fTestsRun(0),
207               fRenderModesEncountered(1) {}
208
209    /**
210     * Assemble shortNamePlusConfig from (surprise!) shortName and configName.
211     *
212     * The method for doing so depends on whether we are using hierarchical naming.
213     * For example, shortName "selftest1" and configName "8888" could be assembled into
214     * either "selftest1_8888" or "8888/selftest1".
215     */
216    SkString make_shortname_plus_config(const char *shortName, const char *configName) {
217        SkString name;
218        if (0 == strlen(configName)) {
219            name.append(shortName);
220        } else if (fUseFileHierarchy) {
221            name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName);
222        } else {
223            name.appendf("%s_%s", shortName, configName);
224        }
225        return name;
226    }
227
228    /**
229     * Assemble filename, suitable for writing out the results of a particular test.
230     */
231    SkString make_filename(const char *path,
232                           const char *shortName,
233                           const char *configName,
234                           const char *renderModeDescriptor,
235                           const char *suffix) {
236        SkString filename = make_shortname_plus_config(shortName, configName);
237        filename.append(renderModeDescriptor);
238        filename.appendUnichar('.');
239        filename.append(suffix);
240        return SkOSPath::SkPathJoin(path, filename.c_str());
241    }
242
243    /**
244     * Assemble filename suitable for writing out an SkBitmap.
245     */
246    SkString make_bitmap_filename(const char *path,
247                                  const char *shortName,
248                                  const char *configName,
249                                  const char *renderModeDescriptor,
250                                  const GmResultDigest &bitmapDigest) {
251        if (fWriteChecksumBasedFilenames) {
252            SkString filename;
253            filename.append(bitmapDigest.getHashType());
254            filename.appendUnichar('_');
255            filename.append(shortName);
256            filename.appendUnichar('_');
257            filename.append(bitmapDigest.getDigestValue());
258            filename.appendUnichar('.');
259            filename.append(kPNG_FileExtension);
260            return SkOSPath::SkPathJoin(path, filename.c_str());
261        } else {
262            return make_filename(path, shortName, configName, renderModeDescriptor,
263                                 kPNG_FileExtension);
264        }
265    }
266
267    /* since PNG insists on unpremultiplying our alpha, we take no
268       precision chances and force all pixels to be 100% opaque,
269       otherwise on compare we may not get a perfect match.
270    */
271    static void force_all_opaque(const SkBitmap& bitmap) {
272        SkBitmap::Config config = bitmap.config();
273        switch (config) {
274        case SkBitmap::kARGB_8888_Config:
275            force_all_opaque_8888(bitmap);
276            break;
277        case SkBitmap::kRGB_565_Config:
278            // nothing to do here; 565 bitmaps are inherently opaque
279            break;
280        default:
281            gm_fprintf(stderr, "unsupported bitmap config %d\n", config);
282            DEBUGFAIL_SEE_STDERR;
283        }
284    }
285
286    static void force_all_opaque_8888(const SkBitmap& bitmap) {
287        SkAutoLockPixels lock(bitmap);
288        for (int y = 0; y < bitmap.height(); y++) {
289            for (int x = 0; x < bitmap.width(); x++) {
290                *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
291            }
292        }
293    }
294
295    static ErrorCombination write_bitmap(const SkString& path, const SkBitmap& bitmap) {
296        // TODO(epoger): Now that we have removed force_all_opaque()
297        // from this method, we should be able to get rid of the
298        // transformation to 8888 format also.
299        SkBitmap copy;
300        bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
301        if (!SkImageEncoder::EncodeFile(path.c_str(), copy,
302                                        SkImageEncoder::kPNG_Type,
303                                        100)) {
304            gm_fprintf(stderr, "FAILED to write bitmap: %s\n", path.c_str());
305            return ErrorCombination(kWritingReferenceImage_ErrorType);
306        }
307        return kEmpty_ErrorCombination;
308    }
309
310    /**
311     * Add all render modes encountered thus far to the "modes" array.
312     */
313    void GetRenderModesEncountered(SkTArray<SkString> &modes) {
314        SkTDict<int>::Iter iter(this->fRenderModesEncountered);
315        const char* mode;
316        while ((mode = iter.next(NULL)) != NULL) {
317            SkString modeAsString = SkString(mode);
318            // TODO(epoger): It seems a bit silly that all of these modes were
319            // recorded with a leading "-" which we have to remove here
320            // (except for mode "", which means plain old original mode).
321            // But that's how renderModeDescriptor has been passed into
322            // compare_test_results_to_reference_bitmap() historically,
323            // and changing that now may affect other parts of our code.
324            if (modeAsString.startsWith("-")) {
325                modeAsString.remove(0, 1);
326                modes.push_back(modeAsString);
327            }
328        }
329    }
330
331    /**
332     * Records the results of this test in fTestsRun and fFailedTests.
333     *
334     * We even record successes, and errors that we regard as
335     * "ignorable"; we can filter them out later.
336     */
337    void RecordTestResults(const ErrorCombination& errorCombination,
338                           const SkString& shortNamePlusConfig,
339                           const char renderModeDescriptor []) {
340        // Things to do regardless of errorCombination.
341        fTestsRun++;
342        int renderModeCount = 0;
343        this->fRenderModesEncountered.find(renderModeDescriptor, &renderModeCount);
344        renderModeCount++;
345        this->fRenderModesEncountered.set(renderModeDescriptor, renderModeCount);
346
347        if (errorCombination.isEmpty()) {
348            return;
349        }
350
351        // Things to do only if there is some error condition.
352        SkString fullName = shortNamePlusConfig;
353        fullName.append(renderModeDescriptor);
354        for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) {
355            ErrorType type = static_cast<ErrorType>(typeInt);
356            if (errorCombination.includes(type)) {
357                fFailedTests[type].push_back(fullName);
358            }
359        }
360    }
361
362    /**
363     * Return the number of significant (non-ignorable) errors we have
364     * encountered so far.
365     */
366    int NumSignificantErrors() {
367        int significantErrors = 0;
368        for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) {
369            ErrorType type = static_cast<ErrorType>(typeInt);
370            if (!fIgnorableErrorTypes.includes(type)) {
371                significantErrors += fFailedTests[type].count();
372            }
373        }
374        return significantErrors;
375    }
376
377    /**
378     * Display the summary of results with this ErrorType.
379     *
380     * @param type which ErrorType
381     * @param verbose whether to be all verbose about it
382     */
383    void DisplayResultTypeSummary(ErrorType type, bool verbose) {
384        bool isIgnorableType = fIgnorableErrorTypes.includes(type);
385
386        SkString line;
387        if (isIgnorableType) {
388            line.append("[ ] ");
389        } else {
390            line.append("[*] ");
391        }
392
393        SkTArray<SkString> *failedTestsOfThisType = &fFailedTests[type];
394        int count = failedTestsOfThisType->count();
395        line.appendf("%d %s", count, getErrorTypeName(type));
396        if (!isIgnorableType || verbose) {
397            line.append(":");
398            for (int i = 0; i < count; ++i) {
399                line.append(" ");
400                line.append((*failedTestsOfThisType)[i]);
401            }
402        }
403        gm_fprintf(stdout, "%s\n", line.c_str());
404    }
405
406    /**
407     * List contents of fFailedTests to stdout.
408     *
409     * @param verbose whether to be all verbose about it
410     */
411    void ListErrors(bool verbose) {
412        // First, print a single summary line.
413        SkString summary;
414        summary.appendf("Ran %d tests:", fTestsRun);
415        for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) {
416            ErrorType type = static_cast<ErrorType>(typeInt);
417            summary.appendf(" %s=%d", getErrorTypeName(type), fFailedTests[type].count());
418        }
419        gm_fprintf(stdout, "%s\n", summary.c_str());
420
421        // Now, for each failure type, list the tests that failed that way.
422        for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) {
423            this->DisplayResultTypeSummary(static_cast<ErrorType>(typeInt), verbose);
424        }
425        gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n");
426    }
427
428    static ErrorCombination write_document(const SkString& path, SkStreamAsset* asset) {
429        SkFILEWStream stream(path.c_str());
430        if (!stream.writeStream(asset, asset->getLength())) {
431            gm_fprintf(stderr, "FAILED to write document: %s\n", path.c_str());
432            return ErrorCombination(kWritingReferenceImage_ErrorType);
433        }
434        return kEmpty_ErrorCombination;
435    }
436
437    /**
438     * Prepare an SkBitmap to render a GM into.
439     *
440     * After you've rendered the GM into the SkBitmap, you must call
441     * complete_bitmap()!
442     *
443     * @todo thudson 22 April 2011 - could refactor this to take in
444     * a factory to generate the context, always call readPixels()
445     * (logically a noop for rasters, if wasted time), and thus collapse the
446     * GPU special case and also let this be used for SkPicture testing.
447     */
448    static void setup_bitmap(const ConfigData& gRec, SkISize& size,
449                             SkBitmap* bitmap) {
450        bitmap->setConfig(gRec.fConfig, size.width(), size.height());
451        bitmap->allocPixels();
452        bitmap->eraseColor(SK_ColorTRANSPARENT);
453    }
454
455    /**
456     * Any finalization steps we need to perform on the SkBitmap after
457     * we have rendered the GM into it.
458     *
459     * It's too bad that we are throwing away alpha channel data
460     * we could otherwise be examining, but this had always been happening
461     * before... it was buried within the compare() method at
462     * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 .
463     *
464     * Apparently we need this, at least for bitmaps that are either:
465     * (a) destined to be written out as PNG files, or
466     * (b) compared against bitmaps read in from PNG files
467     * for the reasons described just above the force_all_opaque() method.
468     *
469     * Neglecting to do this led to the difficult-to-diagnose
470     * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating
471     * spurious pixel_error messages as of r7258')
472     *
473     * TODO(epoger): Come up with a better solution that allows us to
474     * compare full pixel data, including alpha channel, while still being
475     * robust in the face of transformations to/from PNG files.
476     * Options include:
477     *
478     * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that
479     *    will be written to, or compared against, PNG files.
480     *    PRO: Preserve/compare alpha channel info for the non-PNG cases
481     *         (comparing different renderModes in-memory)
482     *    CON: The bitmaps (and hash digests) for these non-PNG cases would be
483     *         different than those for the PNG-compared cases, and in the
484     *         case of a failed renderMode comparison, how would we write the
485     *         image to disk for examination?
486     *
487     * 2. Always compute image hash digests from PNG format (either
488     *    directly from the the bytes of a PNG file, or capturing the
489     *    bytes we would have written to disk if we were writing the
490     *    bitmap out as a PNG).
491     *    PRO: I think this would allow us to never force opaque, and to
492     *         the extent that alpha channel data can be preserved in a PNG
493     *         file, we could observe it.
494     *    CON: If we read a bitmap from disk, we need to take its hash digest
495     *         from the source PNG (we can't compute it from the bitmap we
496     *         read out of the PNG, because we will have already premultiplied
497     *         the alpha).
498     *    CON: Seems wasteful to convert a bitmap to PNG format just to take
499     *         its hash digest. (Although we're wasting lots of effort already
500     *         calling force_all_opaque().)
501     *
502     * 3. Make the alpha premultiply/unpremultiply routines 100% consistent,
503     *    so we can transform images back and forth without fear of off-by-one
504     *    errors.
505     *    CON: Math is hard.
506     *
507     * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each
508     *    channel), rather than demanding absolute equality.
509     *    CON: Can't do this with hash digests.
510     */
511    static void complete_bitmap(SkBitmap* bitmap) {
512        force_all_opaque(*bitmap);
513    }
514
515    static void installFilter(SkCanvas* canvas);
516
517    static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred) {
518        SkAutoCanvasRestore acr(canvas, true);
519
520        if (!isPDF) {
521            canvas->concat(gm->getInitialTransform());
522        }
523        installFilter(canvas);
524        gm->setCanvasIsDeferred(isDeferred);
525        gm->draw(canvas);
526        canvas->setDrawFilter(NULL);
527    }
528
529    static ErrorCombination generate_image(GM* gm, const ConfigData& gRec,
530                                           GrSurface* gpuTarget,
531                                           SkBitmap* bitmap,
532                                           bool deferred) {
533        SkISize size (gm->getISize());
534        setup_bitmap(gRec, size, bitmap);
535
536        SkAutoTUnref<SkCanvas> canvas;
537
538        if (gRec.fBackend == kRaster_Backend) {
539            SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (*bitmap)));
540            if (deferred) {
541                canvas.reset(SkDeferredCanvas::Create(device));
542            } else {
543                canvas.reset(SkNEW_ARGS(SkCanvas, (device)));
544            }
545            invokeGM(gm, canvas, false, deferred);
546            canvas->flush();
547        }
548#if SK_SUPPORT_GPU
549        else {  // GPU
550            SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(gpuTarget));
551            if (deferred) {
552                canvas.reset(SkDeferredCanvas::Create(device));
553            } else {
554                canvas.reset(SkNEW_ARGS(SkCanvas, (device)));
555            }
556            invokeGM(gm, canvas, false, deferred);
557            // the device is as large as the current rendertarget, so
558            // we explicitly only readback the amount we expect (in
559            // size) overwrite our previous allocation
560            bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
561                              size.fHeight);
562            canvas->readPixels(bitmap, 0, 0);
563        }
564#endif
565        complete_bitmap(bitmap);
566        return kEmpty_ErrorCombination;
567    }
568
569    static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
570                                            SkPicture* pict, SkBitmap* bitmap,
571                                            SkScalar scale = SK_Scalar1,
572                                            bool tile = false) {
573        SkISize size = gm->getISize();
574        setup_bitmap(gRec, size, bitmap);
575
576        if (tile) {
577            // Generate the result image by rendering to tiles and accumulating
578            // the results in 'bitmap'
579
580            // This 16x16 tiling matches the settings applied to 'pict' in
581            // 'generate_new_picture'
582            SkISize tileSize = SkISize::Make(16, 16);
583
584            SkBitmap tileBM;
585            setup_bitmap(gRec, tileSize, &tileBM);
586            SkCanvas tileCanvas(tileBM);
587            installFilter(&tileCanvas);
588
589            SkCanvas bmpCanvas(*bitmap);
590            SkPaint bmpPaint;
591            bmpPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
592
593            for (int yTile = 0; yTile < (size.height()+15)/16; ++yTile) {
594                for (int xTile = 0; xTile < (size.width()+15)/16; ++xTile) {
595                    int saveCount = tileCanvas.save();
596                    SkMatrix mat(tileCanvas.getTotalMatrix());
597                    mat.postTranslate(SkIntToScalar(-xTile*tileSize.width()),
598                                      SkIntToScalar(-yTile*tileSize.height()));
599                    tileCanvas.setMatrix(mat);
600                    pict->draw(&tileCanvas);
601                    tileCanvas.flush();
602                    tileCanvas.restoreToCount(saveCount);
603                    bmpCanvas.drawBitmap(tileBM,
604                                         SkIntToScalar(xTile * tileSize.width()),
605                                         SkIntToScalar(yTile * tileSize.height()),
606                                         &bmpPaint);
607                }
608            }
609        } else {
610            SkCanvas canvas(*bitmap);
611            installFilter(&canvas);
612            canvas.scale(scale, scale);
613            canvas.drawPicture(*pict);
614            complete_bitmap(bitmap);
615        }
616    }
617
618    static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
619#ifdef SK_SUPPORT_PDF
620        SkMatrix initialTransform = gm->getInitialTransform();
621        SkISize pageSize = gm->getISize();
622        SkPDFDevice* dev = NULL;
623        if (initialTransform.isIdentity()) {
624            dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
625        } else {
626            SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
627                                            SkIntToScalar(pageSize.height()));
628            initialTransform.mapRect(&content);
629            content.intersect(0, 0, SkIntToScalar(pageSize.width()),
630                              SkIntToScalar(pageSize.height()));
631            SkISize contentSize =
632                SkISize::Make(SkScalarRoundToInt(content.width()),
633                              SkScalarRoundToInt(content.height()));
634            dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
635        }
636        dev->setDCTEncoder(encode_to_dct_stream);
637        SkAutoUnref aur(dev);
638
639        SkCanvas c(dev);
640        invokeGM(gm, &c, true, false);
641
642        SkPDFDocument doc;
643        doc.appendPage(dev);
644        doc.emitPDF(&pdf);
645#endif
646    }
647
648    static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
649#ifdef SK_SUPPORT_XPS
650        SkISize size = gm->getISize();
651
652        SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
653                                       SkIntToScalar(size.height()));
654        static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
655        static const SkScalar upm = 72 * inchesPerMeter;
656        SkVector unitsPerMeter = SkPoint::Make(upm, upm);
657        static const SkScalar ppm = 200 * inchesPerMeter;
658        SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
659
660        SkXPSDevice* dev = new SkXPSDevice();
661        SkAutoUnref aur(dev);
662
663        SkCanvas c(dev);
664        dev->beginPortfolio(&xps);
665        dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
666        invokeGM(gm, &c, false, false);
667        dev->endSheet();
668        dev->endPortfolio();
669
670#endif
671    }
672
673    /**
674     * Log more detail about the mistmatch between expectedBitmap and
675     * actualBitmap.
676     */
677    void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap,
678                             const char *testName) {
679        const int expectedWidth = expectedBitmap.width();
680        const int expectedHeight = expectedBitmap.height();
681        const int width = actualBitmap.width();
682        const int height = actualBitmap.height();
683        if ((expectedWidth != width) || (expectedHeight != height)) {
684            gm_fprintf(stderr, "---- %s: dimension mismatch --"
685                       " expected [%d %d], actual [%d %d]\n",
686                       testName, expectedWidth, expectedHeight, width, height);
687            return;
688        }
689
690        if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) ||
691            (SkBitmap::kARGB_8888_Config != actualBitmap.config())) {
692            gm_fprintf(stderr, "---- %s: not computing max per-channel"
693                       " pixel mismatch because non-8888\n", testName);
694            return;
695        }
696
697        SkAutoLockPixels alp0(expectedBitmap);
698        SkAutoLockPixels alp1(actualBitmap);
699        int errR = 0;
700        int errG = 0;
701        int errB = 0;
702        int errA = 0;
703        int differingPixels = 0;
704
705        for (int y = 0; y < height; ++y) {
706            const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y);
707            const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y);
708            for (int x = 0; x < width; ++x) {
709                SkPMColor expectedPixel = *expectedPixelPtr++;
710                SkPMColor actualPixel = *actualPixelPtr++;
711                if (expectedPixel != actualPixel) {
712                    differingPixels++;
713                    errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) -
714                                                 (int)SkGetPackedR32(actualPixel)));
715                    errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) -
716                                                 (int)SkGetPackedG32(actualPixel)));
717                    errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) -
718                                                 (int)SkGetPackedB32(actualPixel)));
719                    errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) -
720                                                 (int)SkGetPackedA32(actualPixel)));
721                }
722            }
723        }
724        gm_fprintf(stderr, "---- %s: %d (of %d) differing pixels,"
725                   " max per-channel mismatch R=%d G=%d B=%d A=%d\n",
726                   testName, differingPixels, width*height, errR, errG, errB, errA);
727    }
728
729    /**
730     * Compares actual hash digest to expectations, returning the set of errors
731     * (if any) that we saw along the way.
732     *
733     * If fMismatchPath has been set, and there are pixel diffs, then the
734     * actual bitmap will be written out to a file within fMismatchPath.
735     * And similarly for fMissingExpectationsPath...
736     *
737     * @param expectations what expectations to compare actualBitmap against
738     * @param actualBitmapAndDigest the SkBitmap we actually generated, and its GmResultDigest
739     * @param shortName name of test, e.g. "selftest1"
740     * @param configName name of config, e.g. "8888"
741     * @param renderModeDescriptor e.g., "-rtree", "-deferred"
742     * @param addToJsonSummary whether to add these results (both actual and
743     *        expected) to the JSON summary. Regardless of this setting, if
744     *        we find an image mismatch in this test, we will write these
745     *        results to the JSON summary.  (This is so that we will always
746     *        report errors across rendering modes, such as pipe vs tiled.
747     *        See https://codereview.chromium.org/13650002/ )
748     */
749    ErrorCombination compare_to_expectations(Expectations expectations,
750                                             const BitmapAndDigest& actualBitmapAndDigest,
751                                             const char *shortName, const char *configName,
752                                             const char *renderModeDescriptor,
753                                             bool addToJsonSummary) {
754        ErrorCombination errors;
755        SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName);
756        SkString completeNameString(shortNamePlusConfig);
757        completeNameString.append(renderModeDescriptor);
758        completeNameString.append(".");
759        completeNameString.append(kPNG_FileExtension);
760        const char* completeName = completeNameString.c_str();
761
762        if (expectations.empty()) {
763            errors.add(kMissingExpectations_ErrorType);
764
765            // Write out the "actuals" for any tests without expectations, if we have
766            // been directed to do so.
767            if (fMissingExpectationsPath) {
768                SkString path = make_bitmap_filename(fMissingExpectationsPath, shortName,
769                                                     configName, renderModeDescriptor,
770                                                     actualBitmapAndDigest.fDigest);
771                write_bitmap(path, actualBitmapAndDigest.fBitmap);
772            }
773
774        } else if (!expectations.match(actualBitmapAndDigest.fDigest)) {
775            addToJsonSummary = true;
776            // The error mode we record depends on whether this was running
777            // in a non-standard renderMode.
778            if ('\0' == *renderModeDescriptor) {
779                errors.add(kExpectationsMismatch_ErrorType);
780            } else {
781                errors.add(kRenderModeMismatch_ErrorType);
782            }
783
784            // Write out the "actuals" for any mismatches, if we have
785            // been directed to do so.
786            if (fMismatchPath) {
787                SkString path = make_bitmap_filename(fMismatchPath, shortName, configName,
788                                                     renderModeDescriptor,
789                                                     actualBitmapAndDigest.fDigest);
790                write_bitmap(path, actualBitmapAndDigest.fBitmap);
791            }
792
793            // If we have access to a single expected bitmap, log more
794            // detail about the mismatch.
795            const SkBitmap *expectedBitmapPtr = expectations.asBitmap();
796            if (NULL != expectedBitmapPtr) {
797                report_bitmap_diffs(*expectedBitmapPtr, actualBitmapAndDigest.fBitmap,
798                                    completeName);
799            }
800        }
801
802        if (addToJsonSummary) {
803            add_actual_results_to_json_summary(completeName, actualBitmapAndDigest.fDigest, errors,
804                                               expectations.ignoreFailure());
805            add_expected_results_to_json_summary(completeName, expectations);
806        }
807
808        return errors;
809    }
810
811    /**
812     * Add this result to the appropriate JSON collection of actual results,
813     * depending on errors encountered.
814     */
815    void add_actual_results_to_json_summary(const char testName[],
816                                            const GmResultDigest &actualResultDigest,
817                                            ErrorCombination errors,
818                                            bool ignoreFailure) {
819        Json::Value jsonActualResults = actualResultDigest.asJsonTypeValuePair();
820        if (errors.isEmpty()) {
821            this->fJsonActualResults_Succeeded[testName] = jsonActualResults;
822        } else {
823            if (ignoreFailure) {
824                this->fJsonActualResults_FailureIgnored[testName] =
825                    jsonActualResults;
826            } else {
827                if (errors.includes(kMissingExpectations_ErrorType)) {
828                    // TODO: What about the case where there IS an
829                    // expected image hash digest, but that gm test
830                    // doesn't actually run?  For now, those cases
831                    // will always be ignored, because gm only looks
832                    // at expectations that correspond to gm tests
833                    // that were actually run.
834                    //
835                    // Once we have the ability to express
836                    // expectations as a JSON file, we should fix this
837                    // (and add a test case for which an expectation
838                    // is given but the test is never run).
839                    this->fJsonActualResults_NoComparison[testName] =
840                        jsonActualResults;
841                }
842                if (errors.includes(kExpectationsMismatch_ErrorType) ||
843                    errors.includes(kRenderModeMismatch_ErrorType)) {
844                    this->fJsonActualResults_Failed[testName] = jsonActualResults;
845                }
846            }
847        }
848    }
849
850    /**
851     * Add this test to the JSON collection of expected results.
852     */
853    void add_expected_results_to_json_summary(const char testName[],
854                                              Expectations expectations) {
855        this->fJsonExpectedResults[testName] = expectations.asJsonValue();
856    }
857
858    /**
859     * Compare actualBitmap to expectations stored in this->fExpectationsSource.
860     *
861     * @param gm which test generated the actualBitmap
862     * @param gRec
863     * @param configName The config name to look for in the expectation file.
864     * @param actualBitmapAndDigest ptr to bitmap generated by this run, or NULL
865     *        if we don't have a usable bitmap representation
866     */
867    ErrorCombination compare_test_results_to_stored_expectations(
868        GM* gm, const ConfigData& gRec, const char* configName,
869        const BitmapAndDigest* actualBitmapAndDigest) {
870
871        SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), configName);
872
873        ErrorCombination errors;
874
875        if (NULL == actualBitmapAndDigest) {
876            // Note that we intentionally skipped validating the results for
877            // this test, because we don't know how to generate an SkBitmap
878            // version of the output.
879            errors.add(ErrorCombination(kIntentionallySkipped_ErrorType));
880        } else if (!(gRec.fFlags & kWrite_ConfigFlag)) {
881            // We don't record the results for this test or compare them
882            // against any expectations, because the output image isn't
883            // meaningful.
884            // See https://code.google.com/p/skia/issues/detail?id=1410 ('some
885            // GM result images not available for download from Google Storage')
886            errors.add(ErrorCombination(kIntentionallySkipped_ErrorType));
887        } else {
888            ExpectationsSource *expectationsSource = this->fExpectationsSource.get();
889            SkString nameWithExtension(shortNamePlusConfig);
890            nameWithExtension.append(".");
891            nameWithExtension.append(kPNG_FileExtension);
892
893            if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) {
894                /*
895                 * Get the expected results for this test, as one or more allowed
896                 * hash digests. The current implementation of expectationsSource
897                 * get this by computing the hash digest of a single PNG file on disk.
898                 *
899                 * TODO(epoger): This relies on the fact that
900                 * force_all_opaque() was called on the bitmap before it
901                 * was written to disk as a PNG in the first place. If
902                 * not, the hash digest returned here may not match the
903                 * hash digest of actualBitmap, which *has* been run through
904                 * force_all_opaque().
905                 * See comments above complete_bitmap() for more detail.
906                 */
907                Expectations expectations = expectationsSource->get(nameWithExtension.c_str());
908                if (gm->isIgnoringFailures()) {
909                    expectations.setIgnoreFailure(true);
910                }
911                errors.add(compare_to_expectations(expectations, *actualBitmapAndDigest,
912                                                   gm->shortName(), configName, "", true));
913            } else {
914                // If we are running without expectations, we still want to
915                // record the actual results.
916                add_actual_results_to_json_summary(nameWithExtension.c_str(),
917                                                   actualBitmapAndDigest->fDigest,
918                                                   ErrorCombination(kMissingExpectations_ErrorType),
919                                                   false);
920                errors.add(ErrorCombination(kMissingExpectations_ErrorType));
921            }
922        }
923        return errors;
924    }
925
926    /**
927     * Compare actualBitmap to referenceBitmap.
928     *
929     * @param shortName test name, e.g. "selftest1"
930     * @param configName configuration name, e.g. "8888"
931     * @param renderModeDescriptor
932     * @param actualBitmap actual bitmap generated by this run
933     * @param referenceBitmap bitmap we expected to be generated
934     */
935    ErrorCombination compare_test_results_to_reference_bitmap(
936        const char *shortName, const char *configName, const char *renderModeDescriptor,
937        SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) {
938
939        SkASSERT(referenceBitmap);
940        Expectations expectations(*referenceBitmap);
941        BitmapAndDigest actualBitmapAndDigest(actualBitmap);
942
943        // TODO: Eliminate RecordTestResults from here.
944        // Results recording code for the test_drawing path has been refactored so that
945        // RecordTestResults is only called once, at the topmost level. However, the
946        // other paths have not yet been refactored, and RecordTestResults has been added
947        // here to maintain proper behavior for calls not coming from the test_drawing path.
948        ErrorCombination errors;
949        errors.add(compare_to_expectations(expectations, actualBitmapAndDigest, shortName,
950                                           configName, renderModeDescriptor, false));
951        SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName);
952        RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor);
953
954        return errors;
955    }
956
957    static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags,
958                                           SkScalar scale = SK_Scalar1) {
959        // Pictures are refcounted so must be on heap
960        SkPicture* pict;
961        int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale));
962        int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale));
963
964        if (kTileGrid_BbhType == bbhType) {
965            SkTileGridPicture::TileGridInfo info;
966            info.fMargin.setEmpty();
967            info.fOffset.setZero();
968            info.fTileInterval.set(16, 16);
969            pict = new SkTileGridPicture(width, height, info);
970        } else {
971            pict = new SkPicture;
972        }
973        if (kNone_BbhType != bbhType) {
974            recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
975        }
976        SkCanvas* cv = pict->beginRecording(width, height, recordFlags);
977        cv->scale(scale, scale);
978        invokeGM(gm, cv, false, false);
979        pict->endRecording();
980
981        return pict;
982    }
983
984    static SkData* bitmap_encoder(size_t* pixelRefOffset, const SkBitmap& bm) {
985        SkPixelRef* pr = bm.pixelRef();
986        if (pr != NULL) {
987            SkData* data = pr->refEncodedData();
988            if (data != NULL) {
989                *pixelRefOffset = bm.pixelRefOffset();
990                return data;
991            }
992        }
993        return NULL;
994    }
995
996    static SkPicture* stream_to_new_picture(const SkPicture& src) {
997        SkDynamicMemoryWStream storage;
998        src.serialize(&storage, &bitmap_encoder);
999        SkAutoTUnref<SkStreamAsset> pictReadback(storage.detachAsStream());
1000        SkPicture* retval = SkPicture::CreateFromStream(pictReadback,
1001                                                        &SkImageDecoder::DecodeMemory);
1002        return retval;
1003    }
1004
1005    // Test: draw into a bitmap or pdf.
1006    // Depending on flags, possibly compare to an expected image.
1007    // If writePath is not NULL, also write images (or documents) to the specified path.
1008    ErrorCombination test_drawing(GM* gm, const ConfigData& gRec,
1009                                  const SkTDArray<const PDFRasterizerData*> &pdfRasterizers,
1010                                  const char writePath [],
1011                                  GrSurface* gpuTarget,
1012                                  SkBitmap* bitmap) {
1013        ErrorCombination errors;
1014        SkDynamicMemoryWStream document;
1015        SkString path;
1016
1017        if (gRec.fBackend == kRaster_Backend ||
1018            gRec.fBackend == kGPU_Backend) {
1019            // Early exit if we can't generate the image.
1020            errors.add(generate_image(gm, gRec, gpuTarget, bitmap, false));
1021            if (!errors.isEmpty()) {
1022                // TODO: Add a test to exercise what the stdout and
1023                // JSON look like if we get an "early error" while
1024                // trying to generate the image.
1025                return errors;
1026            }
1027            BitmapAndDigest bitmapAndDigest(*bitmap);
1028            errors.add(compare_test_results_to_stored_expectations(
1029                           gm, gRec, gRec.fName, &bitmapAndDigest));
1030
1031            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
1032                path = make_bitmap_filename(writePath, gm->shortName(), gRec.fName,
1033                                            "", bitmapAndDigest.fDigest);
1034                errors.add(write_bitmap(path, bitmapAndDigest.fBitmap));
1035            }
1036        } else if (gRec.fBackend == kPDF_Backend) {
1037            generate_pdf(gm, document);
1038
1039            SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream());
1040            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
1041                path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf");
1042                errors.add(write_document(path, documentStream));
1043            }
1044
1045            if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) {
1046                for (int i = 0; i < pdfRasterizers.count(); i++) {
1047                    SkBitmap pdfBitmap;
1048                    SkASSERT(documentStream->rewind());
1049                    bool success = (*pdfRasterizers[i]->fRasterizerFunction)(
1050                            documentStream.get(), &pdfBitmap);
1051                    if (!success) {
1052                        gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n",
1053                                   gm->shortName(),
1054                                   pdfRasterizers[i]->fName);
1055                        continue;
1056                    }
1057
1058                    SkString configName(gRec.fName);
1059                    configName.append("-");
1060                    configName.append(pdfRasterizers[i]->fName);
1061
1062                    BitmapAndDigest bitmapAndDigest(pdfBitmap);
1063                    errors.add(compare_test_results_to_stored_expectations(
1064                               gm, gRec, configName.c_str(), &bitmapAndDigest));
1065
1066                    if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
1067                        path = make_bitmap_filename(writePath, gm->shortName(), configName.c_str(),
1068                                                    "", bitmapAndDigest.fDigest);
1069                        errors.add(write_bitmap(path, bitmapAndDigest.fBitmap));
1070                    }
1071                }
1072            } else {
1073                errors.add(kIntentionallySkipped_ErrorType);
1074            }
1075        } else if (gRec.fBackend == kXPS_Backend) {
1076            generate_xps(gm, document);
1077            SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream());
1078
1079            errors.add(compare_test_results_to_stored_expectations(
1080                           gm, gRec, gRec.fName, NULL));
1081
1082            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
1083                path = make_filename(writePath, gm->shortName(), gRec.fName, "", "xps");
1084                errors.add(write_document(path, documentStream));
1085            }
1086        } else {
1087            SkASSERT(false);
1088        }
1089        return errors;
1090    }
1091
1092    ErrorCombination test_deferred_drawing(GM* gm,
1093                                           const ConfigData& gRec,
1094                                           const SkBitmap& referenceBitmap,
1095                                           GrSurface* gpuTarget) {
1096        if (gRec.fBackend == kRaster_Backend ||
1097            gRec.fBackend == kGPU_Backend) {
1098            const char renderModeDescriptor[] = "-deferred";
1099            SkBitmap bitmap;
1100            // Early exit if we can't generate the image, but this is
1101            // expected in some cases, so don't report a test failure.
1102            ErrorCombination errors = generate_image(gm, gRec, gpuTarget, &bitmap, true);
1103            // TODO(epoger): This logic is the opposite of what is
1104            // described above... if we succeeded in generating the
1105            // -deferred image, we exit early!  We should fix this
1106            // ASAP, because it is hiding -deferred errors... but for
1107            // now, I'm leaving the logic as it is so that the
1108            // refactoring change
1109            // https://codereview.chromium.org/12992003/ is unblocked.
1110            //
1111            // Filed as https://code.google.com/p/skia/issues/detail?id=1180
1112            // ('image-surface gm test is failing in "deferred" mode,
1113            // and gm is not reporting the failure')
1114            if (errors.isEmpty()) {
1115                // TODO(epoger): Report this as a new ErrorType,
1116                // something like kImageGeneration_ErrorType?
1117                return kEmpty_ErrorCombination;
1118            }
1119            return compare_test_results_to_reference_bitmap(
1120                gm->shortName(), gRec.fName, renderModeDescriptor, bitmap, &referenceBitmap);
1121        }
1122        return kEmpty_ErrorCombination;
1123    }
1124
1125    ErrorCombination test_pipe_playback(GM* gm, const ConfigData& gRec,
1126                                        const SkBitmap& referenceBitmap, bool simulateFailure) {
1127        const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(),
1128                                                                        gRec.fName);
1129        ErrorCombination errors;
1130        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
1131            SkString renderModeDescriptor("-pipe");
1132            renderModeDescriptor.append(gPipeWritingFlagCombos[i].name);
1133
1134            if (gm->getFlags() & GM::kSkipPipe_Flag
1135                || (gPipeWritingFlagCombos[i].flags == SkGPipeWriter::kCrossProcess_Flag
1136                    && gm->getFlags() & GM::kSkipPipeCrossProcess_Flag)) {
1137                RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1138                                  renderModeDescriptor.c_str());
1139                errors.add(kIntentionallySkipped_ErrorType);
1140            } else {
1141                SkBitmap bitmap;
1142                SkISize size = gm->getISize();
1143                setup_bitmap(gRec, size, &bitmap);
1144                SkCanvas canvas(bitmap);
1145                installFilter(&canvas);
1146                // Pass a decoding function so the factory GM (which has an SkBitmap
1147                // with encoded data) will not fail playback.
1148                PipeController pipeController(&canvas, &SkImageDecoder::DecodeMemory);
1149                SkGPipeWriter writer;
1150                SkCanvas* pipeCanvas = writer.startRecording(&pipeController,
1151                                                             gPipeWritingFlagCombos[i].flags,
1152                                                             size.width(), size.height());
1153                if (!simulateFailure) {
1154                    invokeGM(gm, pipeCanvas, false, false);
1155                }
1156                complete_bitmap(&bitmap);
1157                writer.endRecording();
1158                errors.add(compare_test_results_to_reference_bitmap(
1159                    gm->shortName(), gRec.fName, renderModeDescriptor.c_str(), bitmap,
1160                    &referenceBitmap));
1161                if (!errors.isEmpty()) {
1162                    break;
1163                }
1164            }
1165        }
1166        return errors;
1167    }
1168
1169    ErrorCombination test_tiled_pipe_playback(GM* gm, const ConfigData& gRec,
1170                                              const SkBitmap& referenceBitmap) {
1171        const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(),
1172                                                                        gRec.fName);
1173        ErrorCombination errors;
1174        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
1175            SkString renderModeDescriptor("-tiled pipe");
1176            renderModeDescriptor.append(gPipeWritingFlagCombos[i].name);
1177
1178            if ((gm->getFlags() & GM::kSkipPipe_Flag) ||
1179                (gm->getFlags() & GM::kSkipTiled_Flag)) {
1180                RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1181                                  renderModeDescriptor.c_str());
1182                errors.add(kIntentionallySkipped_ErrorType);
1183            } else {
1184                SkBitmap bitmap;
1185                SkISize size = gm->getISize();
1186                setup_bitmap(gRec, size, &bitmap);
1187                SkCanvas canvas(bitmap);
1188                installFilter(&canvas);
1189                TiledPipeController pipeController(bitmap, &SkImageDecoder::DecodeMemory);
1190                SkGPipeWriter writer;
1191                SkCanvas* pipeCanvas = writer.startRecording(&pipeController,
1192                                                             gPipeWritingFlagCombos[i].flags,
1193                                                             size.width(), size.height());
1194                invokeGM(gm, pipeCanvas, false, false);
1195                complete_bitmap(&bitmap);
1196                writer.endRecording();
1197                errors.add(compare_test_results_to_reference_bitmap(gm->shortName(), gRec.fName,
1198                                                                    renderModeDescriptor.c_str(),
1199                                                                    bitmap, &referenceBitmap));
1200                if (!errors.isEmpty()) {
1201                    break;
1202                }
1203            }
1204        }
1205        return errors;
1206    }
1207
1208    //
1209    // member variables.
1210    // They are public for now, to allow easier setting by tool_main().
1211    //
1212
1213    bool fUseFileHierarchy, fWriteChecksumBasedFilenames;
1214    ErrorCombination fIgnorableErrorTypes;
1215
1216    const char* fMismatchPath;
1217    const char* fMissingExpectationsPath;
1218
1219    // collection of tests that have failed with each ErrorType
1220    SkTArray<SkString> fFailedTests[kLast_ErrorType+1];
1221    int fTestsRun;
1222    SkTDict<int> fRenderModesEncountered;
1223
1224    // Where to read expectations (expected image hash digests, etc.) from.
1225    // If unset, we don't do comparisons.
1226    SkAutoTUnref<ExpectationsSource> fExpectationsSource;
1227
1228    // JSON summaries that we generate as we go (just for output).
1229    Json::Value fJsonExpectedResults;
1230    Json::Value fJsonActualResults_Failed;
1231    Json::Value fJsonActualResults_FailureIgnored;
1232    Json::Value fJsonActualResults_NoComparison;
1233    Json::Value fJsonActualResults_Succeeded;
1234
1235}; // end of GMMain class definition
1236
1237#if SK_SUPPORT_GPU
1238static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType;
1239#else
1240static const GLContextType kDontCare_GLContextType = 0;
1241#endif
1242
1243static const ConfigData gRec[] = {
1244    { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "8888",         true },
1245#if 0   // stop testing this (for now at least) since we want to remove support for it (soon please!!!)
1246    { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "4444",         true },
1247#endif
1248    { SkBitmap::kRGB_565_Config,   kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "565",          true },
1249#if SK_SUPPORT_GPU
1250    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType,  0, kRW_ConfigFlag,    "gpu",          true },
1251    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag,    "msaa16",       false},
1252    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType,  4, kRW_ConfigFlag,    "msaa4",        false},
1253    /* The gpudebug context does not generate meaningful images, so don't record
1254     * the images it generates!  We only run it to look for asserts. */
1255    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kDebug_GLContextType,   0, kNone_ConfigFlag,  "gpudebug",     kDebugOnly},
1256    /* The gpunull context does the least amount of work possible and doesn't
1257       generate meaninful images, so don't record them!. It can be run to
1258       isolate the CPU-side processing expense from the GPU-side.
1259      */
1260    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNull_GLContextType,    0, kNone_ConfigFlag,  "gpunull",      kDebugOnly},
1261#if SK_ANGLE
1262    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,   0, kRW_ConfigFlag,    "angle",        true },
1263    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,  16, kRW_ConfigFlag,    "anglemsaa16",  true },
1264#endif // SK_ANGLE
1265#ifdef SK_MESA
1266    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kMESA_GLContextType,    0, kRW_ConfigFlag,    "mesa",         true },
1267#endif // SK_MESA
1268#endif // SK_SUPPORT_GPU
1269#ifdef SK_SUPPORT_XPS
1270    /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */
1271    { SkBitmap::kARGB_8888_Config, kXPS_Backend,    kDontCare_GLContextType,                  0, kWrite_ConfigFlag, "xps",          true },
1272#endif // SK_SUPPORT_XPS
1273#ifdef SK_SUPPORT_PDF
1274    { SkBitmap::kARGB_8888_Config, kPDF_Backend,    kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "pdf",          true },
1275#endif // SK_SUPPORT_PDF
1276};
1277
1278static const PDFRasterizerData kPDFRasterizers[] = {
1279#ifdef SK_BUILD_FOR_MAC
1280    { &SkPDFDocumentToBitmap, "mac",     true },
1281#endif
1282#ifdef SK_BUILD_POPPLER
1283    { &SkPopplerRasterizePDF, "poppler", true },
1284#endif
1285};
1286
1287static const char kDefaultsConfigStr[] = "defaults";
1288static const char kExcludeConfigChar = '~';
1289
1290static SkString configUsage() {
1291    SkString result;
1292    result.appendf("Space delimited list of which configs to run. Possible options: [");
1293    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1294        SkASSERT(gRec[i].fName != kDefaultsConfigStr);
1295        if (i > 0) {
1296            result.append("|");
1297        }
1298        result.appendf("%s", gRec[i].fName);
1299    }
1300    result.append("]\n");
1301    result.appendf("The default value is: \"");
1302    SkString firstDefault;
1303    SkString allButFirstDefaults;
1304    SkString nonDefault;
1305    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1306        if (gRec[i].fRunByDefault) {
1307            if (i > 0) {
1308                result.append(" ");
1309            }
1310            result.append(gRec[i].fName);
1311            if (firstDefault.isEmpty()) {
1312                firstDefault = gRec[i].fName;
1313            } else {
1314                if (!allButFirstDefaults.isEmpty()) {
1315                    allButFirstDefaults.append(" ");
1316                }
1317                allButFirstDefaults.append(gRec[i].fName);
1318            }
1319        } else {
1320            nonDefault = gRec[i].fName;
1321        }
1322    }
1323    result.append("\"\n");
1324    result.appendf("\"%s\" evaluates to the default set of configs.\n", kDefaultsConfigStr);
1325    result.appendf("Prepending \"%c\" on a config name excludes it from the set of configs to run.\n"
1326                   "Exclusions always override inclusions regardless of order.\n",
1327                   kExcludeConfigChar);
1328    result.appendf("E.g. \"--config %s %c%s %s\" will run these configs:\n\t%s %s",
1329                   kDefaultsConfigStr,
1330                   kExcludeConfigChar,
1331                   firstDefault.c_str(),
1332                   nonDefault.c_str(),
1333                   allButFirstDefaults.c_str(),
1334                   nonDefault.c_str());
1335    return result;
1336}
1337
1338static SkString pdfRasterizerUsage() {
1339    SkString result;
1340    result.appendf("Space delimited list of which PDF rasterizers to run. Possible options: [");
1341    // For this (and further) loops through kPDFRasterizers, there is a typecast to int to avoid
1342    // the compiler giving an "comparison of unsigned expression < 0 is always false" warning
1343    // and turning it into a build-breaking error.
1344    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) {
1345        if (i > 0) {
1346            result.append(" ");
1347        }
1348        result.append(kPDFRasterizers[i].fName);
1349    }
1350    result.append("]\n");
1351    result.append("The default value is: \"");
1352    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) {
1353        if (kPDFRasterizers[i].fRunByDefault) {
1354            if (i > 0) {
1355                result.append(" ");
1356            }
1357            result.append(kPDFRasterizers[i].fName);
1358        }
1359    }
1360    result.append("\"");
1361    return result;
1362}
1363
1364// Macro magic to convert a numeric preprocessor token into a string.
1365// Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string
1366// This should probably be moved into one of our common headers...
1367#define TOSTRING_INTERNAL(x) #x
1368#define TOSTRING(x) TOSTRING_INTERNAL(x)
1369
1370// Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath").
1371DEFINE_string(config, "", configUsage().c_str());
1372DEFINE_string(pdfRasterizers, "", pdfRasterizerUsage().c_str());
1373DEFINE_bool(deferred, false, "Exercise the deferred rendering test pass.");
1374DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip.");
1375DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing.");
1376#if SK_SUPPORT_GPU
1377DEFINE_string(gpuCacheSize, "", "<bytes> <count>: Limit the gpu cache to byte size or "
1378              "object count. " TOSTRING(DEFAULT_CACHE_VALUE) " for either value means "
1379              "use the default. 0 for either disables the cache.");
1380#endif
1381DEFINE_bool(hierarchy, false, "Whether to use multilevel directory structure "
1382            "when reading/writing files.");
1383DEFINE_string(ignoreErrorTypes, kDefaultIgnorableErrorTypes.asString(" ").c_str(),
1384              "Space-separated list of ErrorTypes that should be ignored. If any *other* error "
1385              "types are encountered, the tool will exit with a nonzero return value.");
1386DEFINE_string(ignoreTests, "", "Space delimited list of tests for which we should ignore "
1387              "failures.");
1388DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n"
1389              "Multiple matches may be separated by spaces.\n"
1390              "~ causes a matching test to always be skipped\n"
1391              "^ requires the start of the test to match\n"
1392              "$ requires the end of the test to match\n"
1393              "^ and $ requires an exact match\n"
1394              "If a test does not match any list entry,\n"
1395              "it is skipped unless some list entry starts with ~");
1396DEFINE_string(missingExpectationsPath, "", "Write images for tests without expectations "
1397              "into this directory.");
1398DEFINE_string(mismatchPath, "", "Write images for tests that failed due to "
1399              "pixel mismatches into this directory.");
1400DEFINE_string(modulo, "", "[--modulo <remainder> <divisor>]: only run tests for which "
1401              "testIndex %% divisor == remainder.");
1402DEFINE_bool(pdf, true, "Exercise the pdf rendering test pass.");
1403DEFINE_bool(pipe, false, "Exercise the SkGPipe replay test pass.");
1404DEFINE_string2(readPath, r, "", "Read reference images from this dir, and report "
1405               "any differences between those and the newly generated ones.");
1406DEFINE_bool(replay, false, "Exercise the SkPicture replay test pass.");
1407DEFINE_string2(resourcePath, i, "", "Directory that stores image resources.");
1408DEFINE_bool(rtree, false, "Exercise the R-Tree variant of SkPicture test pass.");
1409DEFINE_bool(serialize, false, "Exercise the SkPicture serialization & deserialization test pass.");
1410DEFINE_bool(simulatePipePlaybackFailure, false, "Simulate a rendering failure in pipe mode only.");
1411DEFINE_bool(tiledPipe, false, "Exercise tiled SkGPipe replay.");
1412DEFINE_bool(tileGrid, false, "Exercise the tile grid variant of SkPicture.");
1413DEFINE_string(tileGridReplayScales, "", "Space separated list of floating-point scale "
1414              "factors to be used for tileGrid playback testing. Default value: 1.0");
1415DEFINE_bool2(verbose, v, false, "Give more detail (e.g. list all GMs run, more info about "
1416             "each test).");
1417DEFINE_bool(writeChecksumBasedFilenames, false, "When writing out actual images, use checksum-"
1418            "based filenames, as rebaseline.py will use when downloading them from Google Storage");
1419DEFINE_string(writeJsonSummaryPath, "", "Write a JSON-formatted result summary to this file.");
1420DEFINE_string2(writePath, w, "",  "Write rendered images into this directory.");
1421DEFINE_string2(writePicturePath, p, "", "Write .skp files into this directory.");
1422DEFINE_int32(pdfJpegQuality, -1, "Encodes images in JPEG at quality level N, "
1423             "which can be in range 0-100). N = -1 will disable JPEG compression. "
1424             "Default is N = 100, maximum quality.");
1425
1426// TODO(edisonn): pass a matrix instead of forcePerspectiveMatrix
1427// Either the 9 numbers defining the matrix
1428// or probably more readable would be to replace it with a set of a few predicates
1429// Like --prerotate 100 200 10 --posttranslate 10, 10
1430// Probably define spacial names like centerx, centery, top, bottom, left, right
1431// then we can write something reabable like --rotate centerx centery 90
1432DEFINE_bool(forcePerspectiveMatrix, false, "Force a perspective matrix.");
1433
1434static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) {
1435    // Filter output of warnings that JPEG is not available for the image.
1436    if (bitmap.width() >= 65500 || bitmap.height() >= 65500) return false;
1437    if (FLAGS_pdfJpegQuality == -1) return false;
1438
1439    SkIRect bitmapBounds;
1440    SkBitmap subset;
1441    const SkBitmap* bitmapToUse = &bitmap;
1442    bitmap.getBounds(&bitmapBounds);
1443    if (rect != bitmapBounds) {
1444        SkAssertResult(bitmap.extractSubset(&subset, rect));
1445        bitmapToUse = &subset;
1446    }
1447
1448#if defined(SK_BUILD_FOR_MAC)
1449    // Workaround bug #1043 where bitmaps with referenced pixels cause
1450    // CGImageDestinationFinalize to crash
1451    SkBitmap copy;
1452    bitmapToUse->deepCopyTo(&copy, bitmapToUse->config());
1453    bitmapToUse = &copy;
1454#endif
1455
1456    return SkImageEncoder::EncodeStream(stream,
1457                                        *bitmapToUse,
1458                                        SkImageEncoder::kJPEG_Type,
1459                                        FLAGS_pdfJpegQuality);
1460}
1461
1462static int findConfig(const char config[]) {
1463    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
1464        if (!strcmp(config, gRec[i].fName)) {
1465            return (int) i;
1466        }
1467    }
1468    return -1;
1469}
1470
1471static const PDFRasterizerData* findPDFRasterizer(const char rasterizer[]) {
1472    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); i++) {
1473        if (!strcmp(rasterizer, kPDFRasterizers[i].fName)) {
1474            return &kPDFRasterizers[i];
1475        }
1476    }
1477    return NULL;
1478}
1479
1480template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) {
1481    int index = array->find(value);
1482    if (index < 0) {
1483        *array->append() = value;
1484    }
1485}
1486
1487/**
1488 * Run this test in a number of different configs (8888, 565, PDF,
1489 * etc.), confirming that the resulting bitmaps match expectations
1490 * (which may be different for each config).
1491 *
1492 * Returns all errors encountered while doing so.
1493 */
1494ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm,
1495                                      const SkTDArray<size_t> &configs,
1496                                      const SkTDArray<const PDFRasterizerData*> &pdfRasterizers,
1497                                      GrContextFactory *grFactory);
1498ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm,
1499                                      const SkTDArray<size_t> &configs,
1500                                      const SkTDArray<const PDFRasterizerData*> &pdfRasterizers,
1501                                      GrContextFactory *grFactory) {
1502    const char renderModeDescriptor[] = "";
1503    ErrorCombination errorsForAllConfigs;
1504    uint32_t gmFlags = gm->getFlags();
1505
1506    for (int i = 0; i < configs.count(); i++) {
1507        ConfigData config = gRec[configs[i]];
1508        const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(),
1509                                                                               config.fName);
1510
1511        // Skip any tests that we don't even need to try.
1512        // If any of these were skipped on a per-GM basis, record them as
1513        // kIntentionallySkipped.
1514        if (kPDF_Backend == config.fBackend) {
1515            if (!FLAGS_pdf) {
1516                continue;
1517            }
1518            if (gmFlags & GM::kSkipPDF_Flag) {
1519                gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1520                                         renderModeDescriptor);
1521                errorsForAllConfigs.add(kIntentionallySkipped_ErrorType);
1522                continue;
1523            }
1524        }
1525        if ((gmFlags & GM::kSkip565_Flag) &&
1526            (kRaster_Backend == config.fBackend) &&
1527            (SkBitmap::kRGB_565_Config == config.fConfig)) {
1528            gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1529                                     renderModeDescriptor);
1530            errorsForAllConfigs.add(kIntentionallySkipped_ErrorType);
1531            continue;
1532        }
1533        if (((gmFlags & GM::kSkipGPU_Flag) && kGPU_Backend == config.fBackend) ||
1534            ((gmFlags & GM::kGPUOnly_Flag) && kGPU_Backend != config.fBackend)) {
1535            gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1536                                     renderModeDescriptor);
1537            errorsForAllConfigs.add(kIntentionallySkipped_ErrorType);
1538            continue;
1539        }
1540
1541        // Now we know that we want to run this test and record its
1542        // success or failure.
1543        ErrorCombination errorsForThisConfig;
1544        GrSurface* gpuTarget = NULL;
1545#if SK_SUPPORT_GPU
1546        SkAutoTUnref<GrSurface> auGpuTarget;
1547        if ((errorsForThisConfig.isEmpty()) && (kGPU_Backend == config.fBackend)) {
1548            GrContext* gr = grFactory->get(config.fGLContextType);
1549            bool grSuccess = false;
1550            if (gr) {
1551                // create a render target to back the device
1552                GrTextureDesc desc;
1553                desc.fConfig = kSkia8888_GrPixelConfig;
1554                desc.fFlags = kRenderTarget_GrTextureFlagBit;
1555                desc.fWidth = gm->getISize().width();
1556                desc.fHeight = gm->getISize().height();
1557                desc.fSampleCnt = config.fSampleCnt;
1558                auGpuTarget.reset(gr->createUncachedTexture(desc, NULL, 0));
1559                if (NULL != auGpuTarget) {
1560                    gpuTarget = auGpuTarget;
1561                    grSuccess = true;
1562                    // Set the user specified cache limits if non-default.
1563                    size_t bytes;
1564                    int count;
1565                    gr->getTextureCacheLimits(&count, &bytes);
1566                    if (DEFAULT_CACHE_VALUE != gGpuCacheSizeBytes) {
1567                        bytes = static_cast<size_t>(gGpuCacheSizeBytes);
1568                    }
1569                    if (DEFAULT_CACHE_VALUE != gGpuCacheSizeCount) {
1570                        count = gGpuCacheSizeCount;
1571                    }
1572                    gr->setTextureCacheLimits(count, bytes);
1573                }
1574            }
1575            if (!grSuccess) {
1576                errorsForThisConfig.add(kNoGpuContext_ErrorType);
1577            }
1578        }
1579#endif
1580
1581        SkBitmap comparisonBitmap;
1582
1583        const char* writePath;
1584        if (FLAGS_writePath.count() == 1) {
1585            writePath = FLAGS_writePath[0];
1586        } else {
1587            writePath = NULL;
1588        }
1589
1590        if (errorsForThisConfig.isEmpty()) {
1591            errorsForThisConfig.add(gmmain.test_drawing(gm, config, pdfRasterizers,
1592                                                        writePath, gpuTarget,
1593                                                        &comparisonBitmap));
1594            gmmain.RecordTestResults(errorsForThisConfig, shortNamePlusConfig, "");
1595        }
1596
1597        if (FLAGS_deferred && errorsForThisConfig.isEmpty() &&
1598            (kGPU_Backend == config.fBackend || kRaster_Backend == config.fBackend)) {
1599            errorsForThisConfig.add(gmmain.test_deferred_drawing(gm, config, comparisonBitmap,
1600                                                                 gpuTarget));
1601        }
1602
1603        errorsForAllConfigs.add(errorsForThisConfig);
1604    }
1605    return errorsForAllConfigs;
1606}
1607
1608/**
1609 * Run this test in a number of different drawing modes (pipe,
1610 * deferred, tiled, etc.), confirming that the resulting bitmaps all
1611 * *exactly* match comparisonBitmap.
1612 *
1613 * Returns all errors encountered while doing so.
1614 */
1615ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
1616                                    const SkBitmap &comparisonBitmap,
1617                                    const SkTDArray<SkScalar> &tileGridReplayScales);
1618ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
1619                                    const SkBitmap &comparisonBitmap,
1620                                    const SkTDArray<SkScalar> &tileGridReplayScales) {
1621    ErrorCombination errorsForAllModes;
1622    uint32_t gmFlags = gm->getFlags();
1623    const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(),
1624                                                                           compareConfig.fName);
1625
1626    SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0);
1627    SkAutoUnref aur(pict);
1628    if (FLAGS_replay) {
1629        const char renderModeDescriptor[] = "-replay";
1630        if (gmFlags & GM::kSkipPicture_Flag) {
1631            gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1632                                     renderModeDescriptor);
1633            errorsForAllModes.add(kIntentionallySkipped_ErrorType);
1634        } else {
1635            SkBitmap bitmap;
1636            gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
1637            errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
1638                gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap,
1639                &comparisonBitmap));
1640        }
1641    }
1642
1643    if (FLAGS_serialize) {
1644        const char renderModeDescriptor[] = "-serialize";
1645        if (gmFlags & GM::kSkipPicture_Flag) {
1646            gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1647                                     renderModeDescriptor);
1648            errorsForAllModes.add(kIntentionallySkipped_ErrorType);
1649        } else {
1650            SkPicture* repict = gmmain.stream_to_new_picture(*pict);
1651            SkAutoUnref aurr(repict);
1652            SkBitmap bitmap;
1653            gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap);
1654            errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
1655                gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap,
1656                &comparisonBitmap));
1657        }
1658    }
1659
1660    if ((1 == FLAGS_writePicturePath.count()) &&
1661        !(gmFlags & GM::kSkipPicture_Flag)) {
1662        const char* pictureSuffix = "skp";
1663        // TODO(epoger): Make sure this still works even though the
1664        // filename now contains the config name (it used to contain
1665        // just the shortName).  I think this is actually an
1666        // *improvement*, because now runs with different configs will
1667        // write out their SkPictures to separate files rather than
1668        // overwriting each other.  But we should make sure it doesn't
1669        // break anybody.
1670        SkString path = gmmain.make_filename(FLAGS_writePicturePath[0], gm->shortName(),
1671                                             compareConfig.fName, "", pictureSuffix);
1672        SkFILEWStream stream(path.c_str());
1673        pict->serialize(&stream);
1674    }
1675
1676    if (FLAGS_rtree) {
1677        const char renderModeDescriptor[] = "-rtree";
1678        if (gmFlags & GM::kSkipPicture_Flag) {
1679            gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1680                                     renderModeDescriptor);
1681            errorsForAllModes.add(kIntentionallySkipped_ErrorType);
1682        } else {
1683            SkPicture* pict = gmmain.generate_new_picture(
1684                gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag);
1685            SkAutoUnref aur(pict);
1686            SkBitmap bitmap;
1687            gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
1688            errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
1689                gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap,
1690                &comparisonBitmap));
1691        }
1692    }
1693
1694    if (FLAGS_tileGrid) {
1695        for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) {
1696            SkScalar replayScale = tileGridReplayScales[scaleIndex];
1697            SkString renderModeDescriptor("-tilegrid");
1698            if (SK_Scalar1 != replayScale) {
1699                renderModeDescriptor += "-scale-";
1700                renderModeDescriptor.appendScalar(replayScale);
1701            }
1702
1703            if ((gmFlags & GM::kSkipPicture_Flag) ||
1704                ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)) {
1705                gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig,
1706                                         renderModeDescriptor.c_str());
1707                errorsForAllModes.add(kIntentionallySkipped_ErrorType);
1708            } else {
1709                // We record with the reciprocal scale to obtain a replay
1710                // result that can be validated against comparisonBitmap.
1711                SkScalar recordScale = SkScalarInvert(replayScale);
1712                SkPicture* pict = gmmain.generate_new_picture(
1713                    gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag,
1714                    recordScale);
1715                SkAutoUnref aur(pict);
1716                SkBitmap bitmap;
1717                // We cannot yet pass 'true' to generate_image_from_picture to
1718                // perform actual tiled rendering (see Issue 1198 -
1719                // https://code.google.com/p/skia/issues/detail?id=1198)
1720                gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap,
1721                                                   replayScale /*, true */);
1722                errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
1723                    gm->shortName(), compareConfig.fName, renderModeDescriptor.c_str(), bitmap,
1724                    &comparisonBitmap));
1725            }
1726        }
1727    }
1728
1729    // run the pipe centric GM steps
1730    if (FLAGS_pipe) {
1731        errorsForAllModes.add(gmmain.test_pipe_playback(gm, compareConfig, comparisonBitmap,
1732                                                        FLAGS_simulatePipePlaybackFailure));
1733        if (FLAGS_tiledPipe) {
1734            errorsForAllModes.add(gmmain.test_tiled_pipe_playback(gm, compareConfig,
1735                                                                  comparisonBitmap));
1736        }
1737    }
1738    return errorsForAllModes;
1739}
1740
1741/**
1742 * Return a list of all entries in an array of strings as a single string
1743 * of this form:
1744 * "item1", "item2", "item3"
1745 */
1746SkString list_all(const SkTArray<SkString> &stringArray);
1747SkString list_all(const SkTArray<SkString> &stringArray) {
1748    SkString total;
1749    for (int i = 0; i < stringArray.count(); i++) {
1750        if (i > 0) {
1751            total.append(", ");
1752        }
1753        total.append("\"");
1754        total.append(stringArray[i]);
1755        total.append("\"");
1756    }
1757    return total;
1758}
1759
1760/**
1761 * Return a list of configuration names, as a single string of this form:
1762 * "item1", "item2", "item3"
1763 *
1764 * @param configs configurations, as a list of indices into gRec
1765 */
1766SkString list_all_config_names(const SkTDArray<size_t> &configs);
1767SkString list_all_config_names(const SkTDArray<size_t> &configs) {
1768    SkString total;
1769    for (int i = 0; i < configs.count(); i++) {
1770        if (i > 0) {
1771            total.append(", ");
1772        }
1773        total.append("\"");
1774        total.append(gRec[configs[i]].fName);
1775        total.append("\"");
1776    }
1777    return total;
1778}
1779
1780static bool prepare_subdirectories(const char *root, bool useFileHierarchy,
1781                                   const SkTDArray<size_t> &configs,
1782                                   const SkTDArray<const PDFRasterizerData*>& pdfRasterizers) {
1783    if (!sk_mkdir(root)) {
1784        return false;
1785    }
1786    if (useFileHierarchy) {
1787        for (int i = 0; i < configs.count(); i++) {
1788            ConfigData config = gRec[configs[i]];
1789            SkString subdir;
1790            subdir.appendf("%s%c%s", root, SkPATH_SEPARATOR, config.fName);
1791            if (!sk_mkdir(subdir.c_str())) {
1792                return false;
1793            }
1794
1795            if (config.fBackend == kPDF_Backend) {
1796                for (int j = 0; j < pdfRasterizers.count(); j++) {
1797                    SkString pdfSubdir = subdir;
1798                    pdfSubdir.appendf("-%s", pdfRasterizers[j]->fName);
1799                    if (!sk_mkdir(pdfSubdir.c_str())) {
1800                        return false;
1801                    }
1802                }
1803            }
1804        }
1805    }
1806    return true;
1807}
1808
1809static bool parse_flags_configs(SkTDArray<size_t>* outConfigs,
1810                         GrContextFactory* grFactory) {
1811    SkTDArray<size_t> excludeConfigs;
1812
1813    for (int i = 0; i < FLAGS_config.count(); i++) {
1814        const char* config = FLAGS_config[i];
1815        bool exclude = false;
1816        if (*config == kExcludeConfigChar) {
1817            exclude = true;
1818            config += 1;
1819        }
1820        int index = findConfig(config);
1821        if (index >= 0) {
1822            if (exclude) {
1823                *excludeConfigs.append() = index;
1824            } else {
1825                appendUnique<size_t>(outConfigs, index);
1826            }
1827        } else if (0 == strcmp(kDefaultsConfigStr, config)) {
1828            if (exclude) {
1829                gm_fprintf(stderr, "%c%s is not allowed.\n",
1830                           kExcludeConfigChar, kDefaultsConfigStr);
1831                return false;
1832            }
1833            for (size_t c = 0; c < SK_ARRAY_COUNT(gRec); ++c) {
1834                if (gRec[c].fRunByDefault) {
1835                    appendUnique<size_t>(outConfigs, c);
1836                }
1837            }
1838        } else {
1839            gm_fprintf(stderr, "unrecognized config %s\n", config);
1840            return false;
1841        }
1842    }
1843
1844    for (int i = 0; i < FLAGS_excludeConfig.count(); i++) {
1845        int index = findConfig(FLAGS_excludeConfig[i]);
1846        if (index >= 0) {
1847            *excludeConfigs.append() = index;
1848        } else {
1849            gm_fprintf(stderr, "unrecognized excludeConfig %s\n", FLAGS_excludeConfig[i]);
1850            return false;
1851        }
1852    }
1853
1854    if (outConfigs->count() == 0) {
1855        // if no config is specified by user, add the defaults
1856        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1857            if (gRec[i].fRunByDefault) {
1858                *outConfigs->append() = i;
1859            }
1860        }
1861    }
1862    // now remove any explicitly excluded configs
1863    for (int i = 0; i < excludeConfigs.count(); ++i) {
1864        int index = outConfigs->find(excludeConfigs[i]);
1865        if (index >= 0) {
1866            outConfigs->remove(index);
1867            // now assert that there was only one copy in configs[]
1868            SkASSERT(outConfigs->find(excludeConfigs[i]) < 0);
1869        }
1870    }
1871
1872#if SK_SUPPORT_GPU
1873    SkASSERT(grFactory != NULL);
1874    for (int i = 0; i < outConfigs->count(); ++i) {
1875        size_t index = (*outConfigs)[i];
1876        if (kGPU_Backend == gRec[index].fBackend) {
1877            GrContext* ctx = grFactory->get(gRec[index].fGLContextType);
1878            if (NULL == ctx) {
1879                gm_fprintf(stderr, "GrContext could not be created for config %s."
1880                           " Config will be skipped.\n", gRec[index].fName);
1881                outConfigs->remove(i);
1882                --i;
1883                continue;
1884            }
1885            if (gRec[index].fSampleCnt > ctx->getMaxSampleCount()) {
1886                gm_fprintf(stderr, "Sample count (%d) of config %s is not supported."
1887                           " Config will be skipped.\n",
1888                           gRec[index].fSampleCnt, gRec[index].fName);
1889                outConfigs->remove(i);
1890                --i;
1891            }
1892        }
1893    }
1894#endif
1895
1896    if (outConfigs->isEmpty()) {
1897        gm_fprintf(stderr, "No configs to run.");
1898        return false;
1899    }
1900
1901    // now show the user the set of configs that will be run.
1902    SkString configStr("These configs will be run:");
1903    // show the user the config that will run.
1904    for (int i = 0; i < outConfigs->count(); ++i) {
1905        configStr.appendf(" %s", gRec[(*outConfigs)[i]].fName);
1906    }
1907    gm_fprintf(stdout, "%s\n", configStr.c_str());
1908
1909    return true;
1910}
1911
1912static bool parse_flags_pdf_rasterizers(const SkTDArray<size_t>& configs,
1913                                        SkTDArray<const PDFRasterizerData*>* outRasterizers) {
1914    // No need to run this check (and display the PDF rasterizers message)
1915    // if no PDF backends are in the configs.
1916    bool configHasPDF = false;
1917    for (int i = 0; i < configs.count(); i++) {
1918        if (gRec[configs[i]].fBackend == kPDF_Backend) {
1919            configHasPDF = true;
1920            break;
1921        }
1922    }
1923    if (!configHasPDF) {
1924        return true;
1925    }
1926
1927    for (int i = 0; i < FLAGS_pdfRasterizers.count(); i++) {
1928        const char* rasterizer = FLAGS_pdfRasterizers[i];
1929        const PDFRasterizerData* rasterizerPtr = findPDFRasterizer(rasterizer);
1930
1931        if (rasterizerPtr == NULL) {
1932            gm_fprintf(stderr, "unrecognized rasterizer %s\n", rasterizer);
1933            return false;
1934        }
1935        appendUnique<const PDFRasterizerData*>(outRasterizers,
1936                                               rasterizerPtr);
1937    }
1938
1939    if (outRasterizers->count() == 0) {
1940        // if no config is specified by user, add the defaults
1941        for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) {
1942            if (kPDFRasterizers[i].fRunByDefault) {
1943                *outRasterizers->append() = &kPDFRasterizers[i];
1944            }
1945        }
1946    }
1947
1948    // now show the user the set of configs that will be run.
1949    SkString configStr("These PDF rasterizers will be run:");
1950    // show the user the config that will run.
1951    for (int i = 0; i < outRasterizers->count(); ++i) {
1952        configStr.appendf(" %s", (*outRasterizers)[i]->fName);
1953    }
1954    gm_fprintf(stdout, "%s\n", configStr.c_str());
1955
1956    return true;
1957}
1958
1959static bool parse_flags_ignore_error_types(ErrorCombination* outErrorTypes) {
1960    if (FLAGS_ignoreErrorTypes.count() > 0) {
1961        *outErrorTypes = ErrorCombination();
1962        for (int i = 0; i < FLAGS_ignoreErrorTypes.count(); i++) {
1963            ErrorType type;
1964            const char *name = FLAGS_ignoreErrorTypes[i];
1965            if (!getErrorTypeByName(name, &type)) {
1966                gm_fprintf(stderr, "cannot find ErrorType with name '%s'\n", name);
1967                return false;
1968            } else {
1969                outErrorTypes->add(type);
1970            }
1971        }
1972    }
1973    return true;
1974}
1975
1976static bool parse_flags_modulo(int* moduloRemainder, int* moduloDivisor) {
1977    if (FLAGS_modulo.count() == 2) {
1978        *moduloRemainder = atoi(FLAGS_modulo[0]);
1979        *moduloDivisor = atoi(FLAGS_modulo[1]);
1980        if (*moduloRemainder < 0 || *moduloDivisor <= 0 ||
1981                *moduloRemainder >= *moduloDivisor) {
1982            gm_fprintf(stderr, "invalid modulo values.");
1983            return false;
1984        }
1985    }
1986    return true;
1987}
1988
1989#if SK_SUPPORT_GPU
1990static bool parse_flags_gpu_cache(int* sizeBytes, int* sizeCount) {
1991    if (FLAGS_gpuCacheSize.count() > 0) {
1992        if (FLAGS_gpuCacheSize.count() != 2) {
1993            gm_fprintf(stderr, "--gpuCacheSize requires two arguments\n");
1994            return false;
1995        }
1996        *sizeBytes = atoi(FLAGS_gpuCacheSize[0]);
1997        *sizeCount = atoi(FLAGS_gpuCacheSize[1]);
1998    } else {
1999        *sizeBytes = DEFAULT_CACHE_VALUE;
2000        *sizeCount = DEFAULT_CACHE_VALUE;
2001    }
2002    return true;
2003}
2004#endif
2005
2006static bool parse_flags_tile_grid_replay_scales(SkTDArray<SkScalar>* outScales) {
2007    *outScales->append() = SK_Scalar1; // By default only test at scale 1.0
2008    if (FLAGS_tileGridReplayScales.count() > 0) {
2009        outScales->reset();
2010        for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) {
2011            double val = atof(FLAGS_tileGridReplayScales[i]);
2012            if (0 < val) {
2013                *outScales->append() = SkDoubleToScalar(val);
2014            }
2015        }
2016        if (0 == outScales->count()) {
2017            // Should have at least one scale
2018            gm_fprintf(stderr, "--tileGridReplayScales requires at least one scale.\n");
2019            return false;
2020        }
2021    }
2022    return true;
2023}
2024
2025static bool parse_flags_gmmain_paths(GMMain* gmmain) {
2026    gmmain->fUseFileHierarchy = FLAGS_hierarchy;
2027    gmmain->fWriteChecksumBasedFilenames = FLAGS_writeChecksumBasedFilenames;
2028
2029    if (FLAGS_mismatchPath.count() == 1) {
2030        gmmain->fMismatchPath = FLAGS_mismatchPath[0];
2031    }
2032
2033    if (FLAGS_missingExpectationsPath.count() == 1) {
2034        gmmain->fMissingExpectationsPath = FLAGS_missingExpectationsPath[0];
2035    }
2036
2037    if (FLAGS_readPath.count() == 1) {
2038        const char* readPath = FLAGS_readPath[0];
2039        if (!sk_exists(readPath)) {
2040            gm_fprintf(stderr, "readPath %s does not exist!\n", readPath);
2041            return false;
2042        }
2043        if (sk_isdir(readPath)) {
2044            if (FLAGS_verbose) {
2045                gm_fprintf(stdout, "reading from %s\n", readPath);
2046            }
2047            gmmain->fExpectationsSource.reset(SkNEW_ARGS(
2048                IndividualImageExpectationsSource, (readPath)));
2049        } else {
2050            if (FLAGS_verbose) {
2051                gm_fprintf(stdout, "reading expectations from JSON summary file %s\n", readPath);
2052            }
2053            gmmain->fExpectationsSource.reset(SkNEW_ARGS(JsonExpectationsSource, (readPath)));
2054        }
2055    }
2056    return true;
2057}
2058
2059static bool parse_flags_resource_path() {
2060    if (FLAGS_resourcePath.count() == 1) {
2061        GM::SetResourcePath(FLAGS_resourcePath[0]);
2062    }
2063    return true;
2064}
2065
2066static bool parse_flags_jpeg_quality() {
2067    if (FLAGS_pdfJpegQuality < -1 || FLAGS_pdfJpegQuality > 100) {
2068        gm_fprintf(stderr, "%s\n", "pdfJpegQuality must be in [-1 .. 100] range.");
2069        return false;
2070    }
2071    return true;
2072}
2073
2074int tool_main(int argc, char** argv);
2075int tool_main(int argc, char** argv) {
2076
2077#if SK_ENABLE_INST_COUNT
2078    gPrintInstCount = true;
2079#endif
2080
2081    SkGraphics::Init();
2082    // we don't need to see this during a run
2083    gSkSuppressFontCachePurgeSpew = true;
2084
2085    setSystemPreferences();
2086    GMMain gmmain;
2087
2088    SkString usage;
2089    usage.printf("Run the golden master tests.\n");
2090    SkCommandLineFlags::SetUsage(usage.c_str());
2091    SkCommandLineFlags::Parse(argc, argv);
2092
2093    SkTDArray<size_t> configs;
2094
2095    int moduloRemainder = -1;
2096    int moduloDivisor = -1;
2097    SkTDArray<const PDFRasterizerData*> pdfRasterizers;
2098    SkTDArray<SkScalar> tileGridReplayScales;
2099#if SK_SUPPORT_GPU
2100    GrContextFactory* grFactory = new GrContextFactory;
2101#else
2102    GrContextFactory* grFactory = NULL;
2103#endif
2104
2105    if (!parse_flags_modulo(&moduloRemainder, &moduloDivisor) ||
2106        !parse_flags_ignore_error_types(&gmmain.fIgnorableErrorTypes) ||
2107#if SK_SUPPORT_GPU
2108        !parse_flags_gpu_cache(&gGpuCacheSizeBytes, &gGpuCacheSizeCount) ||
2109#endif
2110        !parse_flags_tile_grid_replay_scales(&tileGridReplayScales) ||
2111        !parse_flags_resource_path() ||
2112        !parse_flags_jpeg_quality() ||
2113        !parse_flags_configs(&configs, grFactory) ||
2114        !parse_flags_pdf_rasterizers(configs, &pdfRasterizers) ||
2115        !parse_flags_gmmain_paths(&gmmain)) {
2116        return -1;
2117    }
2118
2119    if (FLAGS_verbose) {
2120        if (FLAGS_writePath.count() == 1) {
2121            gm_fprintf(stdout, "writing to %s\n", FLAGS_writePath[0]);
2122        }
2123        if (NULL != gmmain.fMismatchPath) {
2124            gm_fprintf(stdout, "writing mismatches to %s\n", gmmain.fMismatchPath);
2125        }
2126        if (NULL != gmmain.fMissingExpectationsPath) {
2127            gm_fprintf(stdout, "writing images without expectations to %s\n",
2128                       gmmain.fMissingExpectationsPath);
2129        }
2130        if (FLAGS_writePicturePath.count() == 1) {
2131            gm_fprintf(stdout, "writing pictures to %s\n", FLAGS_writePicturePath[0]);
2132        }
2133        if (FLAGS_resourcePath.count() == 1) {
2134            gm_fprintf(stdout, "reading resources from %s\n", FLAGS_resourcePath[0]);
2135        }
2136    }
2137
2138    int gmsRun = 0;
2139    int gmIndex = -1;
2140    SkString moduloStr;
2141
2142    // If we will be writing out files, prepare subdirectories.
2143    if (FLAGS_writePath.count() == 1) {
2144        if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHierarchy,
2145                                    configs, pdfRasterizers)) {
2146            return -1;
2147        }
2148    }
2149    if (NULL != gmmain.fMismatchPath) {
2150        if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHierarchy,
2151                                    configs, pdfRasterizers)) {
2152            return -1;
2153        }
2154    }
2155    if (NULL != gmmain.fMissingExpectationsPath) {
2156        if (!prepare_subdirectories(gmmain.fMissingExpectationsPath, gmmain.fUseFileHierarchy,
2157                                    configs, pdfRasterizers)) {
2158            return -1;
2159        }
2160    }
2161
2162    Iter iter;
2163    GM* gm;
2164    while ((gm = iter.next()) != NULL) {
2165        if (FLAGS_forcePerspectiveMatrix) {
2166            SkMatrix perspective;
2167            perspective.setIdentity();
2168            perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
2169            perspective.setSkewX(SkScalarDiv(SkIntToScalar(8),
2170                                 SkIntToScalar(25)));
2171
2172            gm->setStarterMatrix(perspective);
2173        }
2174        SkAutoTDelete<GM> adgm(gm);
2175        ++gmIndex;
2176        if (moduloRemainder >= 0) {
2177            if ((gmIndex % moduloDivisor) != moduloRemainder) {
2178                continue;
2179            }
2180            moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor);
2181        }
2182
2183        const char* shortName = gm->shortName();
2184
2185        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, shortName)) {
2186            continue;
2187        }
2188        if (FLAGS_ignoreTests.contains(shortName)) {
2189            gm->setIgnoreFailures(true);
2190        }
2191
2192        gmsRun++;
2193        SkISize size = gm->getISize();
2194        gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName,
2195                   size.width(), size.height());
2196
2197        run_multiple_configs(gmmain, gm, configs, pdfRasterizers, grFactory);
2198
2199        SkBitmap comparisonBitmap;
2200        const ConfigData compareConfig =
2201            { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison", false };
2202        gmmain.generate_image(gm, compareConfig, NULL, &comparisonBitmap, false);
2203
2204        // TODO(epoger): only run this if gmmain.generate_image() succeeded?
2205        // Otherwise, what are we comparing against?
2206        run_multiple_modes(gmmain, gm, compareConfig, comparisonBitmap, tileGridReplayScales);
2207    }
2208
2209    SkTArray<SkString> modes;
2210    gmmain.GetRenderModesEncountered(modes);
2211    bool reportError = false;
2212    if (gmmain.NumSignificantErrors() > 0) {
2213        reportError = true;
2214    }
2215    int expectedNumberOfTests = gmsRun * (configs.count() + modes.count());
2216
2217    // Output summary to stdout.
2218    if (FLAGS_verbose) {
2219        gm_fprintf(stdout, "Ran %d GMs\n", gmsRun);
2220        gm_fprintf(stdout, "... over %2d configs [%s]\n", configs.count(),
2221                   list_all_config_names(configs).c_str());
2222        gm_fprintf(stdout, "...  and %2d modes   [%s]\n", modes.count(), list_all(modes).c_str());
2223        gm_fprintf(stdout, "... so there should be a total of %d tests.\n", expectedNumberOfTests);
2224    }
2225    gmmain.ListErrors(FLAGS_verbose);
2226
2227    // TODO(epoger): Enable this check for Android, too, once we resolve
2228    // https://code.google.com/p/skia/issues/detail?id=1222
2229    // ('GM is unexpectedly skipping tests on Android')
2230#ifndef SK_BUILD_FOR_ANDROID
2231    if (expectedNumberOfTests != gmmain.fTestsRun) {
2232        gm_fprintf(stderr, "expected %d tests, but ran or skipped %d tests\n",
2233                   expectedNumberOfTests, gmmain.fTestsRun);
2234        reportError = true;
2235    }
2236#endif
2237
2238    if (FLAGS_writeJsonSummaryPath.count() == 1) {
2239        Json::Value root = CreateJsonTree(
2240            gmmain.fJsonExpectedResults,
2241            gmmain.fJsonActualResults_Failed, gmmain.fJsonActualResults_FailureIgnored,
2242            gmmain.fJsonActualResults_NoComparison, gmmain.fJsonActualResults_Succeeded);
2243        std::string jsonStdString = root.toStyledString();
2244        SkFILEWStream stream(FLAGS_writeJsonSummaryPath[0]);
2245        stream.write(jsonStdString.c_str(), jsonStdString.length());
2246    }
2247
2248#if SK_SUPPORT_GPU
2249
2250#if GR_CACHE_STATS
2251    for (int i = 0; i < configs.count(); i++) {
2252        ConfigData config = gRec[configs[i]];
2253
2254        if (FLAGS_verbose && (kGPU_Backend == config.fBackend)) {
2255            GrContext* gr = grFactory->get(config.fGLContextType);
2256
2257            gm_fprintf(stdout, "config: %s %x\n", config.fName, gr);
2258            gr->printCacheStats();
2259        }
2260    }
2261#endif
2262
2263    delete grFactory;
2264#endif
2265    SkGraphics::Term();
2266
2267    return (reportError) ? -1 : 0;
2268}
2269
2270void GMMain::installFilter(SkCanvas* canvas) {
2271    if (FLAGS_forceBWtext) {
2272        canvas->setDrawFilter(SkNEW(BWTextDrawFilter))->unref();
2273    }
2274}
2275
2276#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
2277int main(int argc, char * const argv[]) {
2278    return tool_main(argc, (char**) argv);
2279}
2280#endif
2281