PictureTest.cpp revision e9cd27d4a3c92393cc6c79d4d6f93d266411d95e
1/*
2 * Copyright 2012 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#include "Test.h"
8#include "SkBitmapDevice.h"
9#include "SkCanvas.h"
10#include "SkColorPriv.h"
11#include "SkData.h"
12#include "SkError.h"
13#include "SkPaint.h"
14#include "SkPicture.h"
15#include "SkPictureUtils.h"
16#include "SkRandom.h"
17#include "SkRRect.h"
18#include "SkShader.h"
19#include "SkStream.h"
20
21
22static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
23    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
24    bm->allocPixels();
25    bm->eraseColor(color);
26    if (immutable) {
27        bm->setImmutable();
28    }
29}
30
31typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
32
33static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
34                            const SkPoint& pos) {
35    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
36}
37
38static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
39                                const SkPoint& pos) {
40    SkRect r = {
41        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
42    };
43    r.offset(pos.fX, pos.fY);
44    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
45}
46
47static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
48                            const SkPoint& pos) {
49    SkRect r = {
50        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
51    };
52    r.offset(pos.fX, pos.fY);
53
54    SkShader* s = SkShader::CreateBitmapShader(bm,
55                                               SkShader::kClamp_TileMode,
56                                               SkShader::kClamp_TileMode);
57    SkPaint paint;
58    paint.setShader(s)->unref();
59    canvas->drawRect(r, paint);
60    canvas->drawOval(r, paint);
61    SkRRect rr;
62    rr.setRectXY(r, 10, 10);
63    canvas->drawRRect(rr, paint);
64}
65
66// Return a picture with the bitmaps drawn at the specified positions.
67static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
68                                 int count, DrawBitmapProc proc) {
69    SkPicture* pic = new SkPicture;
70    SkCanvas* canvas = pic->beginRecording(1000, 1000);
71    for (int i = 0; i < count; ++i) {
72        proc(canvas, bm[i], pos[i]);
73    }
74    pic->endRecording();
75    return pic;
76}
77
78static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
79    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
80    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
81    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
82    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
83
84    // we integralize rect to make our tests more predictable, since Gather is
85    // a little sloppy.
86    SkIRect ir;
87    rect->round(&ir);
88    rect->set(ir);
89}
90
91// Allocate result to be large enough to hold subset, and then draw the picture
92// into it, offsetting by subset's top/left corner.
93static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
94    SkIRect ir;
95    subset.roundOut(&ir);
96    int w = ir.width();
97    int h = ir.height();
98    make_bm(result, w, h, 0, false);
99
100    SkCanvas canvas(*result);
101    canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
102    canvas.drawPicture(*pic);
103}
104
105template <typename T> int find_index(const T* array, T elem, int count) {
106    for (int i = 0; i < count; ++i) {
107        if (array[i] == elem) {
108            return i;
109        }
110    }
111    return -1;
112}
113
114// Return true if 'ref' is found in array[]
115static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
116    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
117}
118
119// Look at each pixel in bm, and if its color appears in colors[], find the
120// corresponding value in refs[] and append that ref into array, skipping
121// duplicates of the same value.
122static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
123                               int count, SkTDArray<SkPixelRef*>* array) {
124    // Since we only want to return unique values in array, when we scan we just
125    // set a bit for each index'd color found. In practice we only have a few
126    // distinct colors, so we just use an int's bits as our array. Hence the
127    // assert that count <= number-of-bits-in-our-int.
128    SkASSERT((unsigned)count <= 32);
129    uint32_t bitarray = 0;
130
131    SkAutoLockPixels alp(bm);
132
133    for (int y = 0; y < bm.height(); ++y) {
134        for (int x = 0; x < bm.width(); ++x) {
135            SkPMColor pmc = *bm.getAddr32(x, y);
136            // the only good case where the color is not found would be if
137            // the color is transparent, meaning no bitmap was drawn in that
138            // pixel.
139            if (pmc) {
140                uint32_t index = SkGetPackedR32(pmc);
141                SkASSERT(SkGetPackedG32(pmc) == index);
142                SkASSERT(SkGetPackedB32(pmc) == index);
143                SkASSERT(static_cast<int>(index) < count);
144                bitarray |= 1 << index;
145            }
146        }
147    }
148
149    for (int i = 0; i < count; ++i) {
150        if (bitarray & (1 << i)) {
151            *array->append() = refs[i];
152        }
153    }
154}
155
156static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
157    const int IW = 8;
158    const int IH = IW;
159    const SkScalar W = SkIntToScalar(IW);
160    const SkScalar H = W;
161
162    static const int N = 4;
163    SkBitmap bm[N];
164    SkPixelRef* refs[N];
165
166    const SkPoint pos[] = {
167        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
168    };
169
170    // Our convention is that the color components contain the index of their
171    // corresponding bitmap/pixelref
172    for (int i = 0; i < N; ++i) {
173        make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
174        refs[i] = bm[i].pixelRef();
175    }
176
177    static const DrawBitmapProc procs[] = {
178        drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
179    };
180
181    SkRandom rand;
182    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
183        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
184
185        // quick check for a small piece of each quadrant, which should just
186        // contain 1 bitmap.
187        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
188            SkRect r;
189            r.set(2, 2, W - 2, H - 2);
190            r.offset(pos[i].fX, pos[i].fY);
191            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
192            REPORTER_ASSERT(reporter, data);
193            if (data) {
194                int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
195                REPORTER_ASSERT(reporter, 1 == count);
196                REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
197            }
198        }
199
200        // Test a bunch of random (mostly) rects, and compare the gather results
201        // with a deduced list of refs by looking at the colors drawn.
202        for (int j = 0; j < 100; ++j) {
203            SkRect r;
204            rand_rect(&r, rand, 2*W, 2*H);
205
206            SkBitmap result;
207            draw(pic, r, &result);
208            SkTDArray<SkPixelRef*> array;
209
210            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
211            size_t dataSize = data ? data->size() : 0;
212            int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
213            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
214            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
215            SkAutoDataUnref adu(data);
216
217            gather_from_colors(result, refs, N, &array);
218
219            /*
220             *  GatherPixelRefs is conservative, so it can return more bitmaps
221             *  that we actually can see (usually because of conservative bounds
222             *  inflation for antialiasing). Thus our check here is only that
223             *  Gather didn't miss any that we actually saw. Even that isn't
224             *  a strict requirement on Gather, which is meant to be quick and
225             *  only mostly-correct, but at the moment this test should work.
226             */
227            for (int i = 0; i < array.count(); ++i) {
228                bool found = find(gatherRefs, array[i], gatherCount);
229                REPORTER_ASSERT(reporter, found);
230#if 0
231                // enable this block of code to debug failures, as it will rerun
232                // the case that failed.
233                if (!found) {
234                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
235                    size_t dataSize = data ? data->size() : 0;
236                }
237#endif
238            }
239        }
240    }
241}
242
243#ifdef SK_DEBUG
244// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
245// run in debug mode.
246static void test_deleting_empty_playback() {
247    SkPicture picture;
248    // Creates an SkPictureRecord
249    picture.beginRecording(0, 0);
250    // Turns that into an SkPicturePlayback
251    picture.endRecording();
252    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
253    picture.beginRecording(0, 0);
254}
255
256// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
257static void test_serializing_empty_picture() {
258    SkPicture picture;
259    picture.beginRecording(0, 0);
260    picture.endRecording();
261    SkDynamicMemoryWStream stream;
262    picture.serialize(&stream);
263}
264#endif
265
266static void rand_op(SkCanvas* canvas, SkRandom& rand) {
267    SkPaint paint;
268    SkRect rect = SkRect::MakeWH(50, 50);
269
270    SkScalar unit = rand.nextUScalar1();
271    if (unit <= 0.3) {
272//        SkDebugf("save\n");
273        canvas->save();
274    } else if (unit <= 0.6) {
275//        SkDebugf("restore\n");
276        canvas->restore();
277    } else if (unit <= 0.9) {
278//        SkDebugf("clip\n");
279        canvas->clipRect(rect);
280    } else {
281//        SkDebugf("draw\n");
282        canvas->drawPaint(paint);
283    }
284}
285
286static void test_peephole() {
287    SkRandom rand;
288
289    for (int j = 0; j < 100; j++) {
290        SkRandom rand2(rand); // remember the seed
291
292        SkPicture picture;
293        SkCanvas* canvas = picture.beginRecording(100, 100);
294
295        for (int i = 0; i < 1000; ++i) {
296            rand_op(canvas, rand);
297        }
298        picture.endRecording();
299
300        rand = rand2;
301    }
302
303    {
304        SkPicture picture;
305        SkCanvas* canvas = picture.beginRecording(100, 100);
306        SkRect rect = SkRect::MakeWH(50, 50);
307
308        for (int i = 0; i < 100; ++i) {
309            canvas->save();
310        }
311        while (canvas->getSaveCount() > 1) {
312            canvas->clipRect(rect);
313            canvas->restore();
314        }
315        picture.endRecording();
316    }
317}
318
319#ifndef SK_DEBUG
320// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
321// should never do this.
322static void test_bad_bitmap() {
323    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
324    // fail.
325    SkBitmap bm;
326    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
327    SkPicture picture;
328    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
329    recordingCanvas->drawBitmap(bm, 0, 0);
330    picture.endRecording();
331
332    SkCanvas canvas;
333    canvas.drawPicture(picture);
334}
335#endif
336
337#include "SkData.h"
338#include "SkImageRef_GlobalPool.h"
339// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
340class SkDataImageRef : public SkImageRef_GlobalPool {
341
342public:
343    SkDataImageRef(SkMemoryStream* stream)
344        : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
345        SkASSERT(stream != NULL);
346        fData = stream->copyToData();
347        this->setImmutable();
348    }
349
350    ~SkDataImageRef() {
351        fData->unref();
352    }
353
354    virtual SkData* onRefEncodedData() SK_OVERRIDE {
355        fData->ref();
356        return fData;
357    }
358
359private:
360    SkData* fData;
361};
362
363#include "SkImageEncoder.h"
364
365static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
366    *offset = 0;
367    return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
368}
369
370static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
371    SkPicture picture;
372    SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
373    canvas->drawBitmap(bitmap, 0, 0);
374    SkDynamicMemoryWStream wStream;
375    picture.serialize(&wStream, &encode_bitmap_to_data);
376    return wStream.copyToData();
377}
378
379struct ErrorContext {
380    int fErrors;
381    skiatest::Reporter* fReporter;
382};
383
384static void assert_one_parse_error_cb(SkError error, void* context) {
385    ErrorContext* errorContext = static_cast<ErrorContext*>(context);
386    errorContext->fErrors++;
387    // This test only expects one error, and that is a kParseError. If there are others,
388    // there is some unknown problem.
389    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
390                            "This threw more errors than expected.");
391    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
392                            SkGetLastErrorString());
393}
394
395static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
396    // Create a bitmap that will be encoded.
397    SkBitmap original;
398    make_bm(&original, 100, 100, SK_ColorBLUE, true);
399    SkDynamicMemoryWStream wStream;
400    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
401        return;
402    }
403    SkAutoDataUnref data(wStream.copyToData());
404    SkMemoryStream memStream;
405    memStream.setData(data);
406
407    // Use the encoded bitmap as the data for an image ref.
408    SkBitmap bm;
409    SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
410    imageRef->getInfo(&bm);
411    bm.setPixelRef(imageRef);
412
413    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
414    // Flattening original will follow the old path of performing an encode, while flattening bm
415    // will use the already encoded data.
416    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
417    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
418    REPORTER_ASSERT(reporter, picture1->equals(picture2));
419    // Now test that a parse error was generated when trying to create a new SkPicture without
420    // providing a function to decode the bitmap.
421    ErrorContext context;
422    context.fErrors = 0;
423    context.fReporter = reporter;
424    SkSetErrorCallback(assert_one_parse_error_cb, &context);
425    SkMemoryStream pictureStream(picture1);
426    SkClearLastError();
427    SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
428    REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
429    SkClearLastError();
430    SkSetErrorCallback(NULL, NULL);
431}
432
433static void test_clone_empty(skiatest::Reporter* reporter) {
434    // This is a regression test for crbug.com/172062
435    // Before the fix, we used to crash accessing a null pointer when we
436    // had a picture with no paints. This test passes by not crashing.
437    {
438        SkPicture picture;
439        picture.beginRecording(1, 1);
440        picture.endRecording();
441        SkPicture* destPicture = picture.clone();
442        REPORTER_ASSERT(reporter, NULL != destPicture);
443        destPicture->unref();
444    }
445    {
446        // Test without call to endRecording
447        SkPicture picture;
448        picture.beginRecording(1, 1);
449        SkPicture* destPicture = picture.clone();
450        REPORTER_ASSERT(reporter, NULL != destPicture);
451        destPicture->unref();
452    }
453}
454
455static void test_clip_bound_opt(skiatest::Reporter* reporter) {
456    // Test for crbug.com/229011
457    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
458                                    SkIntToScalar(2), SkIntToScalar(2));
459    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
460                                    SkIntToScalar(1), SkIntToScalar(1));
461    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
462                                    SkIntToScalar(1), SkIntToScalar(1));
463
464    SkPath invPath;
465    invPath.addOval(rect1);
466    invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
467    SkPath path;
468    path.addOval(rect2);
469    SkPath path2;
470    path2.addOval(rect3);
471    SkIRect clipBounds;
472    // Minimalist test set for 100% code coverage of
473    // SkPictureRecord::updateClipConservativelyUsingBounds
474    {
475        SkPicture picture;
476        SkCanvas* canvas = picture.beginRecording(10, 10,
477            SkPicture::kUsePathBoundsForClip_RecordingFlag);
478        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
479        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
480        REPORTER_ASSERT(reporter, true == nonEmpty);
481        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
482        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
483        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
484        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
485    }
486    {
487        SkPicture picture;
488        SkCanvas* canvas = picture.beginRecording(10, 10,
489            SkPicture::kUsePathBoundsForClip_RecordingFlag);
490        canvas->clipPath(path, SkRegion::kIntersect_Op);
491        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
492        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
493        REPORTER_ASSERT(reporter, true == nonEmpty);
494        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
495        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
496        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
497        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
498    }
499    {
500        SkPicture picture;
501        SkCanvas* canvas = picture.beginRecording(10, 10,
502            SkPicture::kUsePathBoundsForClip_RecordingFlag);
503        canvas->clipPath(path, SkRegion::kIntersect_Op);
504        canvas->clipPath(invPath, SkRegion::kUnion_Op);
505        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
506        REPORTER_ASSERT(reporter, true == nonEmpty);
507        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
508        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
509        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
510        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
511    }
512    {
513        SkPicture picture;
514        SkCanvas* canvas = picture.beginRecording(10, 10,
515            SkPicture::kUsePathBoundsForClip_RecordingFlag);
516        canvas->clipPath(path, SkRegion::kDifference_Op);
517        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
518        REPORTER_ASSERT(reporter, true == nonEmpty);
519        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
520        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
521        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
522        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
523    }
524    {
525        SkPicture picture;
526        SkCanvas* canvas = picture.beginRecording(10, 10,
527            SkPicture::kUsePathBoundsForClip_RecordingFlag);
528        canvas->clipPath(path, SkRegion::kReverseDifference_Op);
529        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
530        // True clip is actually empty in this case, but the best
531        // determination we can make using only bounds as input is that the
532        // clip is included in the bounds of 'path'.
533        REPORTER_ASSERT(reporter, true == nonEmpty);
534        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
535        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
536        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
537        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
538    }
539    {
540        SkPicture picture;
541        SkCanvas* canvas = picture.beginRecording(10, 10,
542            SkPicture::kUsePathBoundsForClip_RecordingFlag);
543        canvas->clipPath(path, SkRegion::kIntersect_Op);
544        canvas->clipPath(path2, SkRegion::kXOR_Op);
545        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
546        REPORTER_ASSERT(reporter, true == nonEmpty);
547        REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
548        REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
549        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
550        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
551    }
552}
553
554/**
555 * A canvas that records the number of clip commands.
556 */
557class ClipCountingCanvas : public SkCanvas {
558public:
559    explicit ClipCountingCanvas(SkBaseDevice* device)
560        : SkCanvas(device)
561        , fClipCount(0){
562    }
563
564    virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA)
565        SK_OVERRIDE {
566        fClipCount += 1;
567        return this->INHERITED::clipRect(r, op, doAA);
568    }
569
570    virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
571        SK_OVERRIDE {
572        fClipCount += 1;
573        return this->INHERITED::clipRRect(rrect, op, doAA);
574    }
575
576    virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
577        SK_OVERRIDE {
578        fClipCount += 1;
579        return this->INHERITED::clipPath(path, op, doAA);
580    }
581
582    unsigned getClipCount() const { return fClipCount; }
583
584private:
585    unsigned fClipCount;
586
587    typedef SkCanvas INHERITED;
588};
589
590static void test_clip_expansion(skiatest::Reporter* reporter) {
591    SkPicture picture;
592    SkCanvas* canvas = picture.beginRecording(10, 10, 0);
593
594    canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
595    // The following expanding clip should not be skipped.
596    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
597    // Draw something so the optimizer doesn't just fold the world.
598    SkPaint p;
599    p.setColor(SK_ColorBLUE);
600    canvas->drawPaint(p);
601
602    SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10);
603    ClipCountingCanvas testCanvas(&testDevice);
604    picture.draw(&testCanvas);
605
606    // Both clips should be present on playback.
607    REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
608}
609
610static void TestPicture(skiatest::Reporter* reporter) {
611#ifdef SK_DEBUG
612    test_deleting_empty_playback();
613    test_serializing_empty_picture();
614#else
615    test_bad_bitmap();
616#endif
617    test_peephole();
618    test_gatherpixelrefs(reporter);
619    test_bitmap_with_encoded_data(reporter);
620    test_clone_empty(reporter);
621    test_clip_bound_opt(reporter);
622    test_clip_expansion(reporter);
623}
624
625#include "TestClassDef.h"
626DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
627