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