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