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