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