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