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