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