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