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