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