PictureTest.cpp revision 918e144408ba218df919528f8b48c544f4767883
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 "SkBigPicture.h"
9#include "SkBBoxHierarchy.h"
10#include "SkBlurImageFilter.h"
11#include "SkCanvas.h"
12#include "SkColorMatrixFilter.h"
13#include "SkColorPriv.h"
14#include "SkDashPathEffect.h"
15#include "SkData.h"
16#include "SkImageGenerator.h"
17#include "SkImageEncoder.h"
18#include "SkImageGenerator.h"
19#include "SkMD5.h"
20#include "SkPaint.h"
21#include "SkPicture.h"
22#include "SkPictureAnalyzer.h"
23#include "SkPictureRecorder.h"
24#include "SkPictureUtils.h"
25#include "SkPixelRef.h"
26#include "SkPixelSerializer.h"
27#include "SkMiniRecorder.h"
28#include "SkRRect.h"
29#include "SkRandom.h"
30#include "SkRecord.h"
31#include "SkShader.h"
32#include "SkStream.h"
33#include "sk_tool_utils.h"
34
35#include "Test.h"
36
37#include "SkLumaColorFilter.h"
38#include "SkColorFilterImageFilter.h"
39
40static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
41    bm->allocN32Pixels(w, h);
42    bm->eraseColor(color);
43    if (immutable) {
44        bm->setImmutable();
45    }
46}
47
48// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
49static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
50    // We just need _some_ SkImage
51    const SkPMColor pixel = 0;
52    const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
53    sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel))));
54
55    SkPictureRecorder recorder;
56    recorder.beginRecording(100,100)->drawImage(image, 0,0);
57    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
58
59    REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
60}
61
62/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
63static void test_analysis(skiatest::Reporter* reporter) {
64    SkPictureRecorder recorder;
65
66    SkCanvas* canvas = recorder.beginRecording(100, 100);
67    {
68        canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
69    }
70    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
71    REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
72
73    canvas = recorder.beginRecording(100, 100);
74    {
75        SkPaint paint;
76        // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
77        // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
78        SkBitmap bitmap;
79        bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
80        bitmap.eraseColor(SK_ColorBLUE);
81        *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
82        paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
83                                                   SkShader::kClamp_TileMode));
84        REPORTER_ASSERT(reporter, paint.getShader()->isAImage());
85
86        canvas->drawRect(SkRect::MakeWH(10, 10), paint);
87    }
88    REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps());
89}
90
91
92#ifdef SK_DEBUG
93// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
94// in debug mode, so only run in debug mode.
95static void test_deleting_empty_picture() {
96    SkPictureRecorder recorder;
97    // Creates an SkPictureRecord
98    recorder.beginRecording(0, 0);
99    // Turns that into an SkPicture
100    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
101    // Ceates a new SkPictureRecord
102    recorder.beginRecording(0, 0);
103}
104
105// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
106static void test_serializing_empty_picture() {
107    SkPictureRecorder recorder;
108    recorder.beginRecording(0, 0);
109    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
110    SkDynamicMemoryWStream stream;
111    picture->serialize(&stream);
112}
113#endif
114
115static void rand_op(SkCanvas* canvas, SkRandom& rand) {
116    SkPaint paint;
117    SkRect rect = SkRect::MakeWH(50, 50);
118
119    SkScalar unit = rand.nextUScalar1();
120    if (unit <= 0.3) {
121//        SkDebugf("save\n");
122        canvas->save();
123    } else if (unit <= 0.6) {
124//        SkDebugf("restore\n");
125        canvas->restore();
126    } else if (unit <= 0.9) {
127//        SkDebugf("clip\n");
128        canvas->clipRect(rect);
129    } else {
130//        SkDebugf("draw\n");
131        canvas->drawPaint(paint);
132    }
133}
134
135#if SK_SUPPORT_GPU
136
137static SkPath make_convex_path() {
138    SkPath path;
139    path.lineTo(100, 0);
140    path.lineTo(50, 100);
141    path.close();
142
143    return path;
144}
145
146static SkPath make_concave_path() {
147    SkPath path;
148    path.lineTo(50, 50);
149    path.lineTo(100, 0);
150    path.lineTo(50, 100);
151    path.close();
152
153    return path;
154}
155
156static void test_gpu_veto(skiatest::Reporter* reporter) {
157    SkPictureRecorder recorder;
158
159    SkCanvas* canvas = recorder.beginRecording(100, 100);
160    {
161        SkPath path;
162        path.moveTo(0, 0);
163        path.lineTo(50, 50);
164
165        SkScalar intervals[] = { 1.0f, 1.0f };
166        sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
167
168        SkPaint paint;
169        paint.setStyle(SkPaint::kStroke_Style);
170        paint.setPathEffect(dash);
171
172        for (int i = 0; i < 50; ++i) {
173            canvas->drawPath(path, paint);
174        }
175    }
176    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
177    // path effects currently render an SkPicture undesireable for GPU rendering
178
179    const char *reason = nullptr;
180    REPORTER_ASSERT(reporter,
181        !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason));
182    REPORTER_ASSERT(reporter, reason);
183
184    canvas = recorder.beginRecording(100, 100);
185    {
186        SkPath path;
187
188        path.moveTo(0, 0);
189        path.lineTo(0, 50);
190        path.lineTo(25, 25);
191        path.lineTo(50, 50);
192        path.lineTo(50, 0);
193        path.close();
194        REPORTER_ASSERT(reporter, !path.isConvex());
195
196        SkPaint paint;
197        paint.setAntiAlias(true);
198        for (int i = 0; i < 50; ++i) {
199            canvas->drawPath(path, paint);
200        }
201    }
202    picture = recorder.finishRecordingAsPicture();
203    // A lot of small AA concave paths should be fine for GPU rendering
204    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
205
206    canvas = recorder.beginRecording(100, 100);
207    {
208        SkPath path;
209
210        path.moveTo(0, 0);
211        path.lineTo(0, 100);
212        path.lineTo(50, 50);
213        path.lineTo(100, 100);
214        path.lineTo(100, 0);
215        path.close();
216        REPORTER_ASSERT(reporter, !path.isConvex());
217
218        SkPaint paint;
219        paint.setAntiAlias(true);
220        for (int i = 0; i < 50; ++i) {
221            canvas->drawPath(path, paint);
222        }
223    }
224    picture = recorder.finishRecordingAsPicture();
225    // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
226    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
227
228    canvas = recorder.beginRecording(100, 100);
229    {
230        SkPath path;
231
232        path.moveTo(0, 0);
233        path.lineTo(0, 50);
234        path.lineTo(25, 25);
235        path.lineTo(50, 50);
236        path.lineTo(50, 0);
237        path.close();
238        REPORTER_ASSERT(reporter, !path.isConvex());
239
240        SkPaint paint;
241        paint.setAntiAlias(true);
242        paint.setStyle(SkPaint::kStroke_Style);
243        paint.setStrokeWidth(0);
244        for (int i = 0; i < 50; ++i) {
245            canvas->drawPath(path, paint);
246        }
247    }
248    picture = recorder.finishRecordingAsPicture();
249    // hairline stroked AA concave paths are fine for GPU rendering
250    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
251
252    canvas = recorder.beginRecording(100, 100);
253    {
254        SkPaint paint;
255        SkScalar intervals [] = { 10, 20 };
256        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
257
258        SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
259
260        for (int i = 0; i < 50; ++i) {
261            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
262        }
263    }
264    picture = recorder.finishRecordingAsPicture();
265    // fast-path dashed effects are fine for GPU rendering ...
266    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
267
268    canvas = recorder.beginRecording(100, 100);
269    {
270        SkPaint paint;
271        SkScalar intervals [] = { 10, 20 };
272        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
273
274        for (int i = 0; i < 50; ++i) {
275            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
276        }
277    }
278    picture = recorder.finishRecordingAsPicture();
279    // ... but only when applied to drawPoint() calls
280    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
281
282    canvas = recorder.beginRecording(100, 100);
283    {
284        const SkPath convexClip = make_convex_path();
285        const SkPath concaveClip = make_concave_path();
286
287        for (int i = 0; i < 50; ++i) {
288            canvas->clipPath(convexClip);
289            canvas->clipPath(concaveClip);
290            canvas->clipPath(convexClip, kIntersect_SkClipOp, true);
291            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
292        }
293    }
294    picture = recorder.finishRecordingAsPicture();
295    // Convex clips and non-AA concave clips are fine on the GPU.
296    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
297
298    canvas = recorder.beginRecording(100, 100);
299    {
300        const SkPath concaveClip = make_concave_path();
301        for (int i = 0; i < 50; ++i) {
302            canvas->clipPath(concaveClip, kIntersect_SkClipOp, true);
303            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
304        }
305    }
306    picture = recorder.finishRecordingAsPicture();
307    // ... but AA concave clips are not.
308    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
309
310    // Nest the previous picture inside a new one.
311    canvas = recorder.beginRecording(100, 100);
312    {
313        canvas->drawPicture(picture);
314    }
315    picture = recorder.finishRecordingAsPicture();
316    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
317}
318
319#endif // SK_SUPPORT_GPU
320
321static void set_canvas_to_save_count_4(SkCanvas* canvas) {
322    canvas->restoreToCount(1);
323    canvas->save();
324    canvas->save();
325    canvas->save();
326}
327
328/**
329 * A canvas that records the number of saves, saveLayers and restores.
330 */
331class SaveCountingCanvas : public SkCanvas {
332public:
333    SaveCountingCanvas(int width, int height)
334        : INHERITED(width, height)
335        , fSaveCount(0)
336        , fSaveLayerCount(0)
337        , fRestoreCount(0){
338    }
339
340    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
341        ++fSaveLayerCount;
342        return this->INHERITED::getSaveLayerStrategy(rec);
343    }
344
345    void willSave() override {
346        ++fSaveCount;
347        this->INHERITED::willSave();
348    }
349
350    void willRestore() override {
351        ++fRestoreCount;
352        this->INHERITED::willRestore();
353    }
354
355    unsigned int getSaveCount() const { return fSaveCount; }
356    unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
357    unsigned int getRestoreCount() const { return fRestoreCount; }
358
359private:
360    unsigned int fSaveCount;
361    unsigned int fSaveLayerCount;
362    unsigned int fRestoreCount;
363
364    typedef SkCanvas INHERITED;
365};
366
367void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
368                      unsigned int numSaves, unsigned int numSaveLayers,
369                      unsigned int numRestores) {
370    SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
371                              SkScalarCeilToInt(picture->cullRect().height()));
372
373    picture->playback(&canvas);
374
375    // Optimizations may have removed these,
376    // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
377    REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
378    REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
379    REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
380}
381
382// This class exists so SkPicture can friend it and give it access to
383// the 'partialReplay' method.
384class SkPictureRecorderReplayTester {
385public:
386    static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
387        SkPictureRecorder recorder2;
388
389        SkCanvas* canvas = recorder2.beginRecording(10, 10);
390
391        recorder->partialReplay(canvas);
392
393        return recorder2.finishRecordingAsPicture();
394    }
395};
396
397static void create_imbalance(SkCanvas* canvas) {
398    SkRect clipRect = SkRect::MakeWH(2, 2);
399    SkRect drawRect = SkRect::MakeWH(10, 10);
400    canvas->save();
401        canvas->clipRect(clipRect, kReplace_SkClipOp);
402        canvas->translate(1.0f, 1.0f);
403        SkPaint p;
404        p.setColor(SK_ColorGREEN);
405        canvas->drawRect(drawRect, p);
406    // no restore
407}
408
409// This tests that replaying a potentially unbalanced picture into a canvas
410// doesn't affect the canvas' save count or matrix/clip state.
411static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
412    SkBitmap bm;
413    bm.allocN32Pixels(4, 3);
414    SkCanvas canvas(bm);
415
416    int beforeSaveCount = canvas.getSaveCount();
417
418    SkMatrix beforeMatrix = canvas.getTotalMatrix();
419
420    SkRect beforeClip = canvas.getLocalClipBounds();
421
422    canvas.drawPicture(picture);
423
424    REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
425    REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
426
427    SkRect afterClip = canvas.getLocalClipBounds();
428
429    REPORTER_ASSERT(reporter, afterClip == beforeClip);
430}
431
432// Test out SkPictureRecorder::partialReplay
433DEF_TEST(PictureRecorder_replay, reporter) {
434    // check save/saveLayer state
435    {
436        SkPictureRecorder recorder;
437
438        SkCanvas* canvas = recorder.beginRecording(10, 10);
439
440        canvas->saveLayer(nullptr, nullptr);
441
442        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
443
444        // The extra save and restore comes from the Copy process.
445        check_save_state(reporter, copy.get(), 2, 1, 3);
446
447        canvas->saveLayer(nullptr, nullptr);
448
449        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
450
451        check_save_state(reporter, final.get(), 1, 2, 3);
452
453        // The copy shouldn't pick up any operations added after it was made
454        check_save_state(reporter, copy.get(), 2, 1, 3);
455    }
456
457    // (partially) check leakage of draw ops
458    {
459        SkPictureRecorder recorder;
460
461        SkCanvas* canvas = recorder.beginRecording(10, 10);
462
463        SkRect r = SkRect::MakeWH(5, 5);
464        SkPaint p;
465
466        canvas->drawRect(r, p);
467
468        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
469
470        REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
471
472        SkBitmap bm;
473        make_bm(&bm, 10, 10, SK_ColorRED, true);
474
475        r.offset(5.0f, 5.0f);
476        canvas->drawBitmapRect(bm, r, nullptr);
477
478        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
479        REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
480
481        REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
482
483        // The snapshot shouldn't pick up any operations added after it was made
484        REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
485    }
486
487    // Recreate the Android partialReplay test case
488    {
489        SkPictureRecorder recorder;
490
491        SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
492        create_imbalance(canvas);
493
494        int expectedSaveCount = canvas->getSaveCount();
495
496        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
497        check_balance(reporter, copy.get());
498
499        REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
500
501        // End the recording of source to test the picture finalization
502        // process isn't complicated by the partialReplay step
503        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
504    }
505}
506
507static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
508    SkCanvas testCanvas(100, 100);
509    set_canvas_to_save_count_4(&testCanvas);
510
511    REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
512
513    SkPaint paint;
514    SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
515
516    SkPictureRecorder recorder;
517
518    {
519        // Create picture with 2 unbalanced saves
520        SkCanvas* canvas = recorder.beginRecording(100, 100);
521        canvas->save();
522        canvas->translate(10, 10);
523        canvas->drawRect(rect, paint);
524        canvas->save();
525        canvas->translate(10, 10);
526        canvas->drawRect(rect, paint);
527        sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
528
529        testCanvas.drawPicture(extraSavePicture);
530        REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
531    }
532
533    set_canvas_to_save_count_4(&testCanvas);
534
535    {
536        // Create picture with 2 unbalanced restores
537        SkCanvas* canvas = recorder.beginRecording(100, 100);
538        canvas->save();
539        canvas->translate(10, 10);
540        canvas->drawRect(rect, paint);
541        canvas->save();
542        canvas->translate(10, 10);
543        canvas->drawRect(rect, paint);
544        canvas->restore();
545        canvas->restore();
546        canvas->restore();
547        canvas->restore();
548        sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
549
550        testCanvas.drawPicture(extraRestorePicture);
551        REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
552    }
553
554    set_canvas_to_save_count_4(&testCanvas);
555
556    {
557        SkCanvas* canvas = recorder.beginRecording(100, 100);
558        canvas->translate(10, 10);
559        canvas->drawRect(rect, paint);
560        sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
561
562        testCanvas.drawPicture(noSavePicture);
563        REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
564        REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
565    }
566}
567
568static void test_peephole() {
569    SkRandom rand;
570
571    SkPictureRecorder recorder;
572
573    for (int j = 0; j < 100; j++) {
574        SkRandom rand2(rand); // remember the seed
575
576        SkCanvas* canvas = recorder.beginRecording(100, 100);
577
578        for (int i = 0; i < 1000; ++i) {
579            rand_op(canvas, rand);
580        }
581        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
582
583        rand = rand2;
584    }
585
586    {
587        SkCanvas* canvas = recorder.beginRecording(100, 100);
588        SkRect rect = SkRect::MakeWH(50, 50);
589
590        for (int i = 0; i < 100; ++i) {
591            canvas->save();
592        }
593        while (canvas->getSaveCount() > 1) {
594            canvas->clipRect(rect);
595            canvas->restore();
596        }
597        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
598    }
599}
600
601#ifndef SK_DEBUG
602// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
603// should never do this.
604static void test_bad_bitmap() {
605    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
606    // fail.
607    SkBitmap bm;
608    bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
609    SkPictureRecorder recorder;
610    SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
611    recordingCanvas->drawBitmap(bm, 0, 0);
612    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
613
614    SkCanvas canvas;
615    canvas.drawPicture(picture);
616}
617#endif
618
619static void test_clip_bound_opt(skiatest::Reporter* reporter) {
620    // Test for crbug.com/229011
621    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
622                                    SkIntToScalar(2), SkIntToScalar(2));
623    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
624                                    SkIntToScalar(1), SkIntToScalar(1));
625    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
626                                    SkIntToScalar(1), SkIntToScalar(1));
627
628    SkPath invPath;
629    invPath.addOval(rect1);
630    invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
631    SkPath path;
632    path.addOval(rect2);
633    SkPath path2;
634    path2.addOval(rect3);
635    SkIRect clipBounds;
636    SkPictureRecorder recorder;
637
638    // Testing conservative-raster-clip that is enabled by PictureRecord
639    {
640        SkCanvas* canvas = recorder.beginRecording(10, 10);
641        canvas->clipPath(invPath);
642        clipBounds = canvas->getDeviceClipBounds();
643        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
644        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
645        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
646        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
647    }
648    {
649        SkCanvas* canvas = recorder.beginRecording(10, 10);
650        canvas->clipPath(path);
651        canvas->clipPath(invPath);
652        clipBounds = canvas->getDeviceClipBounds();
653        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
654        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
655        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
656        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
657    }
658    {
659        SkCanvas* canvas = recorder.beginRecording(10, 10);
660        canvas->clipPath(path);
661        canvas->clipPath(invPath, kUnion_SkClipOp);
662        clipBounds = canvas->getDeviceClipBounds();
663        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
664        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
665        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
666        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
667    }
668    {
669        SkCanvas* canvas = recorder.beginRecording(10, 10);
670        canvas->clipPath(path, kDifference_SkClipOp);
671        clipBounds = canvas->getDeviceClipBounds();
672        REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
673        REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
674        REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
675        REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
676    }
677    {
678        SkCanvas* canvas = recorder.beginRecording(10, 10);
679        canvas->clipPath(path, kReverseDifference_SkClipOp);
680        clipBounds = canvas->getDeviceClipBounds();
681        // True clip is actually empty in this case, but the best
682        // determination we can make using only bounds as input is that the
683        // clip is included in the bounds of 'path'.
684        REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
685        REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
686        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
687        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
688    }
689    {
690        SkCanvas* canvas = recorder.beginRecording(10, 10);
691        canvas->clipPath(path, kIntersect_SkClipOp);
692        canvas->clipPath(path2, kXOR_SkClipOp);
693        clipBounds = canvas->getDeviceClipBounds();
694        REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
695        REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
696        REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
697        REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
698    }
699}
700
701static void test_cull_rect_reset(skiatest::Reporter* reporter) {
702    SkPictureRecorder recorder;
703    SkRect bounds = SkRect::MakeWH(10, 10);
704    SkRTreeFactory factory;
705    SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
706    bounds = SkRect::MakeWH(100, 100);
707    SkPaint paint;
708    canvas->drawRect(bounds, paint);
709    canvas->drawRect(bounds, paint);
710    sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
711    const SkBigPicture* picture = p->asSkBigPicture();
712    REPORTER_ASSERT(reporter, picture);
713
714    SkRect finalCullRect = picture->cullRect();
715    REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
716    REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
717    REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
718    REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
719
720    const SkBBoxHierarchy* pictureBBH = picture->bbh();
721    SkRect bbhCullRect = pictureBBH->getRootBound();
722    REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
723    REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
724    REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
725    REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
726}
727
728
729/**
730 * A canvas that records the number of clip commands.
731 */
732class ClipCountingCanvas : public SkCanvas {
733public:
734    ClipCountingCanvas(int width, int height)
735        : INHERITED(width, height)
736        , fClipCount(0){
737    }
738
739    void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
740        fClipCount += 1;
741        this->INHERITED::onClipRect(r, op, edgeStyle);
742    }
743
744    void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
745        fClipCount += 1;
746        this->INHERITED::onClipRRect(rrect, op, edgeStyle);
747    }
748
749    void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
750        fClipCount += 1;
751        this->INHERITED::onClipPath(path, op, edgeStyle);
752    }
753
754    void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
755        fClipCount += 1;
756        this->INHERITED::onClipRegion(deviceRgn, op);
757    }
758
759    unsigned getClipCount() const { return fClipCount; }
760
761private:
762    unsigned fClipCount;
763
764    typedef SkCanvas INHERITED;
765};
766
767static void test_clip_expansion(skiatest::Reporter* reporter) {
768    SkPictureRecorder recorder;
769    SkCanvas* canvas = recorder.beginRecording(10, 10);
770
771    canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
772    // The following expanding clip should not be skipped.
773    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
774    // Draw something so the optimizer doesn't just fold the world.
775    SkPaint p;
776    p.setColor(SK_ColorBLUE);
777    canvas->drawPaint(p);
778    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
779
780    ClipCountingCanvas testCanvas(10, 10);
781    picture->playback(&testCanvas);
782
783    // Both clips should be present on playback.
784    REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
785}
786
787static void test_hierarchical(skiatest::Reporter* reporter) {
788    SkBitmap bm;
789    make_bm(&bm, 10, 10, SK_ColorRED, true);
790
791    SkPictureRecorder recorder;
792
793    recorder.beginRecording(10, 10);
794    sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
795    REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
796
797    recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
798    sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
799    REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
800
801    {
802        SkCanvas* canvas = recorder.beginRecording(10, 10);
803        canvas->drawPicture(childPlain);
804        sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
805        REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
806    }
807    {
808        SkCanvas* canvas = recorder.beginRecording(10, 10);
809        canvas->drawPicture(childWithBitmap);
810        sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
811        REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
812    }
813    {
814        SkCanvas* canvas = recorder.beginRecording(10, 10);
815        canvas->drawBitmap(bm, 0, 0);
816        canvas->drawPicture(childPlain);
817        sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
818        REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
819    }
820    {
821        SkCanvas* canvas = recorder.beginRecording(10, 10);
822        canvas->drawBitmap(bm, 0, 0);
823        canvas->drawPicture(childWithBitmap);
824        sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
825        REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
826    }
827}
828
829static void test_gen_id(skiatest::Reporter* reporter) {
830
831    SkPictureRecorder recorder;
832    recorder.beginRecording(0, 0);
833    sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
834
835    // Empty pictures should still have a valid ID
836    REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
837
838    SkCanvas* canvas = recorder.beginRecording(1, 1);
839    canvas->drawARGB(255, 255, 255, 255);
840    sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
841    // picture should have a non-zero id after recording
842    REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
843
844    // both pictures should have different ids
845    REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
846}
847
848static void test_typeface(skiatest::Reporter* reporter) {
849    SkPictureRecorder recorder;
850    SkCanvas* canvas = recorder.beginRecording(10, 10);
851    SkPaint paint;
852    paint.setTypeface(SkTypeface::MakeFromName("Arial",
853                                               SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
854    canvas->drawText("Q", 1, 0, 10, paint);
855    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
856    SkDynamicMemoryWStream stream;
857    picture->serialize(&stream);
858}
859
860DEF_TEST(Picture, reporter) {
861    test_typeface(reporter);
862#ifdef SK_DEBUG
863    test_deleting_empty_picture();
864    test_serializing_empty_picture();
865#else
866    test_bad_bitmap();
867#endif
868    test_unbalanced_save_restores(reporter);
869    test_peephole();
870#if SK_SUPPORT_GPU
871    test_gpu_veto(reporter);
872#endif
873    test_images_are_found_by_willPlayBackBitmaps(reporter);
874    test_analysis(reporter);
875    test_clip_bound_opt(reporter);
876    test_clip_expansion(reporter);
877    test_hierarchical(reporter);
878    test_gen_id(reporter);
879    test_cull_rect_reset(reporter);
880}
881
882static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
883    const SkPaint paint;
884    const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
885    const SkIRect irect =  { 2, 2, 3, 3 };
886    int divs[] = { 2, 3 };
887    SkCanvas::Lattice lattice;
888    lattice.fXCount = lattice.fYCount = 2;
889    lattice.fXDivs = lattice.fYDivs = divs;
890
891    // Don't care what these record, as long as they're legal.
892    canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
893    canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
894    canvas->drawBitmapNine(bitmap, irect, rect, &paint);
895    canvas->drawBitmap(bitmap, 1, 1);   // drawSprite
896    canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
897}
898
899static void test_draw_bitmaps(SkCanvas* canvas) {
900    SkBitmap empty;
901    draw_bitmaps(empty, canvas);
902    empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
903    draw_bitmaps(empty, canvas);
904}
905
906DEF_TEST(Picture_EmptyBitmap, r) {
907    SkPictureRecorder recorder;
908    test_draw_bitmaps(recorder.beginRecording(10, 10));
909    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
910}
911
912DEF_TEST(Canvas_EmptyBitmap, r) {
913    SkBitmap dst;
914    dst.allocN32Pixels(10, 10);
915    SkCanvas canvas(dst);
916
917    test_draw_bitmaps(&canvas);
918}
919
920DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
921    // This test is from crbug.com/344987.
922    // The commands are:
923    //   saveLayer with paint that modifies alpha
924    //     drawBitmapRect
925    //     drawBitmapRect
926    //   restore
927    // The bug was that this structure was modified so that:
928    //  - The saveLayer and restore were eliminated
929    //  - The alpha was only applied to the first drawBitmapRectToRect
930
931    // This test draws blue and red squares inside a 50% transparent
932    // layer.  Both colours should show up muted.
933    // When the bug is present, the red square (the second bitmap)
934    // shows upwith full opacity.
935
936    SkBitmap blueBM;
937    make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
938    SkBitmap redBM;
939    make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
940    SkPaint semiTransparent;
941    semiTransparent.setAlpha(0x80);
942
943    SkPictureRecorder recorder;
944    SkCanvas* canvas = recorder.beginRecording(100, 100);
945    canvas->drawARGB(0, 0, 0, 0);
946
947    canvas->saveLayer(0, &semiTransparent);
948    canvas->drawBitmap(blueBM, 25, 25);
949    canvas->drawBitmap(redBM, 50, 50);
950    canvas->restore();
951
952    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
953
954    // Now replay the picture back on another canvas
955    // and check a couple of its pixels.
956    SkBitmap replayBM;
957    make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
958    SkCanvas replayCanvas(replayBM);
959    picture->playback(&replayCanvas);
960    replayCanvas.flush();
961
962    // With the bug present, at (55, 55) we would get a fully opaque red
963    // intead of a dark red.
964    REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
965    REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
966}
967
968struct CountingBBH : public SkBBoxHierarchy {
969    mutable int searchCalls;
970    SkRect rootBound;
971
972    CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
973
974    void search(const SkRect& query, SkTDArray<int>* results) const override {
975        this->searchCalls++;
976    }
977
978    void insert(const SkRect[], int) override {}
979    virtual size_t bytesUsed() const override { return 0; }
980    SkRect getRootBound() const override { return rootBound; }
981};
982
983class SpoonFedBBHFactory : public SkBBHFactory {
984public:
985    explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
986    SkBBoxHierarchy* operator()(const SkRect&) const override {
987        return SkRef(fBBH);
988    }
989private:
990    SkBBoxHierarchy* fBBH;
991};
992
993// When the canvas clip covers the full picture, we don't need to call the BBH.
994DEF_TEST(Picture_SkipBBH, r) {
995    SkRect bound = SkRect::MakeWH(320, 240);
996    CountingBBH bbh(bound);
997    SpoonFedBBHFactory factory(&bbh);
998
999    SkPictureRecorder recorder;
1000    SkCanvas* c = recorder.beginRecording(bound, &factory);
1001    // Record a few ops so we don't hit a small- or empty- picture optimization.
1002        c->drawRect(bound, SkPaint());
1003        c->drawRect(bound, SkPaint());
1004    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1005
1006    SkCanvas big(640, 480), small(300, 200);
1007
1008    picture->playback(&big);
1009    REPORTER_ASSERT(r, bbh.searchCalls == 0);
1010
1011    picture->playback(&small);
1012    REPORTER_ASSERT(r, bbh.searchCalls == 1);
1013}
1014
1015DEF_TEST(Picture_BitmapLeak, r) {
1016    SkBitmap mut, immut;
1017    mut.allocN32Pixels(300, 200);
1018    immut.allocN32Pixels(300, 200);
1019    immut.setImmutable();
1020    SkASSERT(!mut.isImmutable());
1021    SkASSERT(immut.isImmutable());
1022
1023    // No one can hold a ref on our pixels yet.
1024    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1025    REPORTER_ASSERT(r, immut.pixelRef()->unique());
1026
1027    sk_sp<SkPicture> pic;
1028    {
1029        // we want the recorder to go out of scope before our subsequent checks, so we
1030        // place it inside local braces.
1031        SkPictureRecorder rec;
1032        SkCanvas* canvas = rec.beginRecording(1920, 1200);
1033            canvas->drawBitmap(mut, 0, 0);
1034            canvas->drawBitmap(immut, 800, 600);
1035        pic = rec.finishRecordingAsPicture();
1036    }
1037
1038    // The picture shares the immutable pixels but copies the mutable ones.
1039    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1040    REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1041
1042    // When the picture goes away, it's just our bitmaps holding the refs.
1043    pic = nullptr;
1044    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1045    REPORTER_ASSERT(r, immut.pixelRef()->unique());
1046}
1047
1048// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1049DEF_TEST(Picture_getRecordingCanvas, r) {
1050    SkPictureRecorder rec;
1051    REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1052    for (int i = 0; i < 3; i++) {
1053        rec.beginRecording(100, 100);
1054        REPORTER_ASSERT(r, rec.getRecordingCanvas());
1055        rec.finishRecordingAsPicture();
1056        REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1057    }
1058}
1059
1060DEF_TEST(MiniRecorderLeftHanging, r) {
1061    // Any shader or other ref-counted effect will do just fine here.
1062    SkPaint paint;
1063    paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
1064
1065    SkMiniRecorder rec;
1066    REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1067    // Don't call rec.detachPicture().  Test succeeds by not asserting or leaking the shader.
1068}
1069
1070DEF_TEST(Picture_preserveCullRect, r) {
1071    SkPictureRecorder recorder;
1072
1073    SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1074    c->clear(SK_ColorCYAN);
1075
1076    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1077    SkDynamicMemoryWStream wstream;
1078    picture->serialize(&wstream);
1079
1080    std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
1081    sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
1082
1083    REPORTER_ASSERT(r, deserializedPicture != nullptr);
1084    REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1085    REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1086    REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1087    REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1088}
1089
1090#if SK_SUPPORT_GPU
1091
1092DEF_TEST(PictureGpuAnalyzer, r) {
1093    SkPictureRecorder recorder;
1094
1095    {
1096        SkCanvas* canvas = recorder.beginRecording(10, 10);
1097        SkPaint paint;
1098        SkScalar intervals [] = { 10, 20 };
1099        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1100
1101        for (int i = 0; i < 50; ++i) {
1102            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1103        }
1104    }
1105    sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1106
1107    SkPictureGpuAnalyzer analyzer;
1108    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1109
1110    analyzer.analyzePicture(vetoPicture.get());
1111    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1112
1113    analyzer.reset();
1114    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1115
1116    recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1117    sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1118
1119    analyzer.analyzePicture(nestedVetoPicture.get());
1120    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1121
1122    analyzer.reset();
1123
1124    const SkPath convexClip = make_convex_path();
1125    const SkPath concaveClip = make_concave_path();
1126    for (int i = 0; i < 50; ++i) {
1127        analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false);
1128        analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true);
1129        analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false);
1130    }
1131    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1132
1133    for (int i = 0; i < 50; ++i) {
1134        analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true);
1135    }
1136    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1137}
1138
1139#endif // SK_SUPPORT_GPU
1140
1141///////////////////////////////////////////////////////////////////////////////////////////////////
1142
1143// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1144#if 0
1145static void empty_ops(SkCanvas* canvas) {
1146}
1147static void clip_ops(SkCanvas* canvas) {
1148    canvas->save();
1149    canvas->clipRect(SkRect::MakeWH(20, 20));
1150    canvas->restore();
1151}
1152static void matrix_ops(SkCanvas* canvas) {
1153    canvas->save();
1154    canvas->scale(2, 3);
1155    canvas->restore();
1156}
1157static void matrixclip_ops(SkCanvas* canvas) {
1158    canvas->save();
1159    canvas->scale(2, 3);
1160    canvas->clipRect(SkRect::MakeWH(20, 20));
1161    canvas->restore();
1162}
1163typedef void (*CanvasProc)(SkCanvas*);
1164
1165// Test the kReturnNullForEmpty_FinishFlag option when recording
1166//
1167DEF_TEST(Picture_RecordEmpty, r) {
1168    const SkRect cull = SkRect::MakeWH(100, 100);
1169
1170    CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1171
1172    for (auto proc : procs) {
1173        {
1174            SkPictureRecorder rec;
1175            proc(rec.beginRecording(cull));
1176            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1177            REPORTER_ASSERT(r, pic.get());
1178            REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1179        }
1180        {
1181            SkPictureRecorder rec;
1182            proc(rec.beginRecording(cull));
1183            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1184                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1185            REPORTER_ASSERT(r, !pic.get());
1186        }
1187        {
1188            SkPictureRecorder rec;
1189            proc(rec.beginRecording(cull));
1190            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1191            REPORTER_ASSERT(r, dr.get());
1192        }
1193        {
1194            SkPictureRecorder rec;
1195            proc(rec.beginRecording(cull));
1196            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1197                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1198            REPORTER_ASSERT(r, !dr.get());
1199        }
1200    }
1201}
1202#endif
1203
1204