PictureTest.cpp revision 627778d5ba4fd6f4a4a1238bbf7a1b561469fe21
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()->isAImage());
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, SkCanvas::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, SkCanvas::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, SkCanvas::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 sk_sp<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.detachAsData();
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    sk_sp<SkData> data = wStream.detachAsData();
674
675    SkBitmap bm;
676    bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data.get(), &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    sk_sp<SkData> picture1(serialized_picture_from_bitmap(original));
683    sk_sp<SkData> picture2(serialized_picture_from_bitmap(bm));
684    REPORTER_ASSERT(reporter, picture1->equals(picture2.get()));
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(std::move(picture1));
693    SkClearLastError();
694    sk_sp<SkPicture> pictureFromStream(SkPicture::MakeFromStream(&pictureStream));
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);
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);
751        canvas->clipPath(invPath);
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);
762        canvas->clipPath(invPath, SkCanvas::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, SkCanvas::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, SkCanvas::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, SkCanvas::kIntersect_Op);
796        canvas->clipPath(path2, SkCanvas::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    void onClipRect(const SkRect& r, ClipOp op, ClipEdgeStyle edgeStyle) override {
845        fClipCount += 1;
846        this->INHERITED::onClipRect(r, op, edgeStyle);
847    }
848
849    void onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle)override {
850        fClipCount += 1;
851        this->INHERITED::onClipRRect(rrect, op, edgeStyle);
852    }
853
854    void onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) override {
855        fClipCount += 1;
856        this->INHERITED::onClipPath(path, op, edgeStyle);
857    }
858
859    void onClipRegion(const SkRegion& deviceRgn, ClipOp op) override {
860        fClipCount += 1;
861        this->INHERITED::onClipRegion(deviceRgn, op);
862    }
863
864    unsigned getClipCount() const { return fClipCount; }
865
866private:
867    unsigned fClipCount;
868
869    typedef SkCanvas INHERITED;
870};
871
872static void test_clip_expansion(skiatest::Reporter* reporter) {
873    SkPictureRecorder recorder;
874    SkCanvas* canvas = recorder.beginRecording(10, 10);
875
876    canvas->clipRect(SkRect::MakeEmpty(), SkCanvas::kReplace_Op);
877    // The following expanding clip should not be skipped.
878    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkCanvas::kUnion_Op);
879    // Draw something so the optimizer doesn't just fold the world.
880    SkPaint p;
881    p.setColor(SK_ColorBLUE);
882    canvas->drawPaint(p);
883    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
884
885    ClipCountingCanvas testCanvas(10, 10);
886    picture->playback(&testCanvas);
887
888    // Both clips should be present on playback.
889    REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
890}
891
892static void test_hierarchical(skiatest::Reporter* reporter) {
893    SkBitmap bm;
894    make_bm(&bm, 10, 10, SK_ColorRED, true);
895
896    SkPictureRecorder recorder;
897
898    recorder.beginRecording(10, 10);
899    sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
900    REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
901
902    recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
903    sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
904    REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
905
906    {
907        SkCanvas* canvas = recorder.beginRecording(10, 10);
908        canvas->drawPicture(childPlain);
909        sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
910        REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
911    }
912    {
913        SkCanvas* canvas = recorder.beginRecording(10, 10);
914        canvas->drawPicture(childWithBitmap);
915        sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
916        REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
917    }
918    {
919        SkCanvas* canvas = recorder.beginRecording(10, 10);
920        canvas->drawBitmap(bm, 0, 0);
921        canvas->drawPicture(childPlain);
922        sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
923        REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
924    }
925    {
926        SkCanvas* canvas = recorder.beginRecording(10, 10);
927        canvas->drawBitmap(bm, 0, 0);
928        canvas->drawPicture(childWithBitmap);
929        sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
930        REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
931    }
932}
933
934static void test_gen_id(skiatest::Reporter* reporter) {
935
936    SkPictureRecorder recorder;
937    recorder.beginRecording(0, 0);
938    sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
939
940    // Empty pictures should still have a valid ID
941    REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
942
943    SkCanvas* canvas = recorder.beginRecording(1, 1);
944    canvas->drawARGB(255, 255, 255, 255);
945    sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
946    // picture should have a non-zero id after recording
947    REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
948
949    // both pictures should have different ids
950    REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
951}
952
953static void test_typeface(skiatest::Reporter* reporter) {
954    SkPictureRecorder recorder;
955    SkCanvas* canvas = recorder.beginRecording(10, 10);
956    SkPaint paint;
957    paint.setTypeface(SkTypeface::MakeFromName("Arial",
958                                               SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
959    canvas->drawText("Q", 1, 0, 10, paint);
960    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
961    SkDynamicMemoryWStream stream;
962    picture->serialize(&stream);
963}
964
965DEF_TEST(Picture, reporter) {
966    test_typeface(reporter);
967#ifdef SK_DEBUG
968    test_deleting_empty_picture();
969    test_serializing_empty_picture();
970#else
971    test_bad_bitmap();
972#endif
973    test_unbalanced_save_restores(reporter);
974    test_peephole();
975#if SK_SUPPORT_GPU
976    test_gpu_veto(reporter);
977#endif
978    test_images_are_found_by_willPlayBackBitmaps(reporter);
979    test_analysis(reporter);
980    test_clip_bound_opt(reporter);
981    test_clip_expansion(reporter);
982    test_hierarchical(reporter);
983    test_gen_id(reporter);
984    test_cull_rect_reset(reporter);
985}
986
987static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
988    const SkPaint paint;
989    const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
990    const SkIRect irect =  { 2, 2, 3, 3 };
991    int divs[] = { 2, 3 };
992    SkCanvas::Lattice lattice;
993    lattice.fXCount = lattice.fYCount = 2;
994    lattice.fXDivs = lattice.fYDivs = divs;
995
996    // Don't care what these record, as long as they're legal.
997    canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
998    canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
999    canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1000    canvas->drawBitmap(bitmap, 1, 1);   // drawSprite
1001    canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
1002}
1003
1004static void test_draw_bitmaps(SkCanvas* canvas) {
1005    SkBitmap empty;
1006    draw_bitmaps(empty, canvas);
1007    empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
1008    draw_bitmaps(empty, canvas);
1009}
1010
1011DEF_TEST(Picture_EmptyBitmap, r) {
1012    SkPictureRecorder recorder;
1013    test_draw_bitmaps(recorder.beginRecording(10, 10));
1014    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1015}
1016
1017DEF_TEST(Canvas_EmptyBitmap, r) {
1018    SkBitmap dst;
1019    dst.allocN32Pixels(10, 10);
1020    SkCanvas canvas(dst);
1021
1022    test_draw_bitmaps(&canvas);
1023}
1024
1025DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1026    // This test is from crbug.com/344987.
1027    // The commands are:
1028    //   saveLayer with paint that modifies alpha
1029    //     drawBitmapRect
1030    //     drawBitmapRect
1031    //   restore
1032    // The bug was that this structure was modified so that:
1033    //  - The saveLayer and restore were eliminated
1034    //  - The alpha was only applied to the first drawBitmapRectToRect
1035
1036    // This test draws blue and red squares inside a 50% transparent
1037    // layer.  Both colours should show up muted.
1038    // When the bug is present, the red square (the second bitmap)
1039    // shows upwith full opacity.
1040
1041    SkBitmap blueBM;
1042    make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1043    SkBitmap redBM;
1044    make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1045    SkPaint semiTransparent;
1046    semiTransparent.setAlpha(0x80);
1047
1048    SkPictureRecorder recorder;
1049    SkCanvas* canvas = recorder.beginRecording(100, 100);
1050    canvas->drawARGB(0, 0, 0, 0);
1051
1052    canvas->saveLayer(0, &semiTransparent);
1053    canvas->drawBitmap(blueBM, 25, 25);
1054    canvas->drawBitmap(redBM, 50, 50);
1055    canvas->restore();
1056
1057    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1058
1059    // Now replay the picture back on another canvas
1060    // and check a couple of its pixels.
1061    SkBitmap replayBM;
1062    make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1063    SkCanvas replayCanvas(replayBM);
1064    picture->playback(&replayCanvas);
1065    replayCanvas.flush();
1066
1067    // With the bug present, at (55, 55) we would get a fully opaque red
1068    // intead of a dark red.
1069    REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1070    REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1071}
1072
1073struct CountingBBH : public SkBBoxHierarchy {
1074    mutable int searchCalls;
1075    SkRect rootBound;
1076
1077    CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
1078
1079    void search(const SkRect& query, SkTDArray<int>* results) const override {
1080        this->searchCalls++;
1081    }
1082
1083    void insert(const SkRect[], int) override {}
1084    virtual size_t bytesUsed() const override { return 0; }
1085    SkRect getRootBound() const override { return rootBound; }
1086};
1087
1088class SpoonFedBBHFactory : public SkBBHFactory {
1089public:
1090    explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
1091    SkBBoxHierarchy* operator()(const SkRect&) const override {
1092        return SkRef(fBBH);
1093    }
1094private:
1095    SkBBoxHierarchy* fBBH;
1096};
1097
1098// When the canvas clip covers the full picture, we don't need to call the BBH.
1099DEF_TEST(Picture_SkipBBH, r) {
1100    SkRect bound = SkRect::MakeWH(320, 240);
1101    CountingBBH bbh(bound);
1102    SpoonFedBBHFactory factory(&bbh);
1103
1104    SkPictureRecorder recorder;
1105    SkCanvas* c = recorder.beginRecording(bound, &factory);
1106    // Record a few ops so we don't hit a small- or empty- picture optimization.
1107        c->drawRect(bound, SkPaint());
1108        c->drawRect(bound, SkPaint());
1109    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1110
1111    SkCanvas big(640, 480), small(300, 200);
1112
1113    picture->playback(&big);
1114    REPORTER_ASSERT(r, bbh.searchCalls == 0);
1115
1116    picture->playback(&small);
1117    REPORTER_ASSERT(r, bbh.searchCalls == 1);
1118}
1119
1120DEF_TEST(Picture_BitmapLeak, r) {
1121    SkBitmap mut, immut;
1122    mut.allocN32Pixels(300, 200);
1123    immut.allocN32Pixels(300, 200);
1124    immut.setImmutable();
1125    SkASSERT(!mut.isImmutable());
1126    SkASSERT(immut.isImmutable());
1127
1128    // No one can hold a ref on our pixels yet.
1129    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1130    REPORTER_ASSERT(r, immut.pixelRef()->unique());
1131
1132    sk_sp<SkPicture> pic;
1133    {
1134        // we want the recorder to go out of scope before our subsequent checks, so we
1135        // place it inside local braces.
1136        SkPictureRecorder rec;
1137        SkCanvas* canvas = rec.beginRecording(1920, 1200);
1138            canvas->drawBitmap(mut, 0, 0);
1139            canvas->drawBitmap(immut, 800, 600);
1140        pic = rec.finishRecordingAsPicture();
1141    }
1142
1143    // The picture shares the immutable pixels but copies the mutable ones.
1144    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1145    REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1146
1147    // When the picture goes away, it's just our bitmaps holding the refs.
1148    pic = nullptr;
1149    REPORTER_ASSERT(r, mut.pixelRef()->unique());
1150    REPORTER_ASSERT(r, immut.pixelRef()->unique());
1151}
1152
1153// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1154DEF_TEST(Picture_getRecordingCanvas, r) {
1155    SkPictureRecorder rec;
1156    REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1157    for (int i = 0; i < 3; i++) {
1158        rec.beginRecording(100, 100);
1159        REPORTER_ASSERT(r, rec.getRecordingCanvas());
1160        rec.finishRecordingAsPicture();
1161        REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1162    }
1163}
1164
1165DEF_TEST(MiniRecorderLeftHanging, r) {
1166    // Any shader or other ref-counted effect will do just fine here.
1167    SkPaint paint;
1168    paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
1169
1170    SkMiniRecorder rec;
1171    REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1172    // Don't call rec.detachPicture().  Test succeeds by not asserting or leaking the shader.
1173}
1174
1175DEF_TEST(Picture_preserveCullRect, r) {
1176    SkPictureRecorder recorder;
1177
1178    SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1179    c->clear(SK_ColorCYAN);
1180
1181    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1182    SkDynamicMemoryWStream wstream;
1183    picture->serialize(&wstream);
1184
1185    SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1186    sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream));
1187
1188    REPORTER_ASSERT(r, deserializedPicture != nullptr);
1189    REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1190    REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1191    REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1192    REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1193}
1194
1195#if SK_SUPPORT_GPU
1196
1197DEF_TEST(PictureGpuAnalyzer, r) {
1198    SkPictureRecorder recorder;
1199
1200    {
1201        SkCanvas* canvas = recorder.beginRecording(10, 10);
1202        SkPaint paint;
1203        SkScalar intervals [] = { 10, 20 };
1204        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1205
1206        for (int i = 0; i < 50; ++i) {
1207            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1208        }
1209    }
1210    sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1211
1212    SkPictureGpuAnalyzer analyzer;
1213    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1214
1215    analyzer.analyzePicture(vetoPicture.get());
1216    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1217
1218    analyzer.reset();
1219    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1220
1221    recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1222    sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1223
1224    analyzer.analyzePicture(nestedVetoPicture.get());
1225    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1226
1227    analyzer.reset();
1228
1229    const SkPath convexClip = make_convex_path();
1230    const SkPath concaveClip = make_concave_path();
1231    for (int i = 0; i < 50; ++i) {
1232        analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, false);
1233        analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, true);
1234        analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, false);
1235    }
1236    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1237
1238    for (int i = 0; i < 50; ++i) {
1239        analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, true);
1240    }
1241    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1242}
1243
1244#endif // SK_SUPPORT_GPU
1245
1246///////////////////////////////////////////////////////////////////////////////////////////////////
1247
1248// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1249#if 0
1250static void empty_ops(SkCanvas* canvas) {
1251}
1252static void clip_ops(SkCanvas* canvas) {
1253    canvas->save();
1254    canvas->clipRect(SkRect::MakeWH(20, 20));
1255    canvas->restore();
1256}
1257static void matrix_ops(SkCanvas* canvas) {
1258    canvas->save();
1259    canvas->scale(2, 3);
1260    canvas->restore();
1261}
1262static void matrixclip_ops(SkCanvas* canvas) {
1263    canvas->save();
1264    canvas->scale(2, 3);
1265    canvas->clipRect(SkRect::MakeWH(20, 20));
1266    canvas->restore();
1267}
1268typedef void (*CanvasProc)(SkCanvas*);
1269
1270// Test the kReturnNullForEmpty_FinishFlag option when recording
1271//
1272DEF_TEST(Picture_RecordEmpty, r) {
1273    const SkRect cull = SkRect::MakeWH(100, 100);
1274
1275    CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1276
1277    for (auto proc : procs) {
1278        {
1279            SkPictureRecorder rec;
1280            proc(rec.beginRecording(cull));
1281            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1282            REPORTER_ASSERT(r, pic.get());
1283            REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1284        }
1285        {
1286            SkPictureRecorder rec;
1287            proc(rec.beginRecording(cull));
1288            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1289                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1290            REPORTER_ASSERT(r, !pic.get());
1291        }
1292        {
1293            SkPictureRecorder rec;
1294            proc(rec.beginRecording(cull));
1295            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1296            REPORTER_ASSERT(r, dr.get());
1297        }
1298        {
1299            SkPictureRecorder rec;
1300            proc(rec.beginRecording(cull));
1301            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1302                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1303            REPORTER_ASSERT(r, !dr.get());
1304        }
1305    }
1306}
1307#endif
1308
1309