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