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