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