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