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