• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkBBHFactory.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkClipOp.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkFontStyle.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPath.h"
19 #include "include/core/SkPictureRecorder.h"
20 #include "include/core/SkPixelRef.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkScalar.h"
24 #include "include/core/SkShader.h"
25 #include "include/core/SkStream.h"
26 #include "include/core/SkTypeface.h"
27 #include "include/core/SkTypes.h"
28 #include "include/utils/SkRandom.h"
29 #include "src/core/SkBigPicture.h"
30 #include "src/core/SkMiniRecorder.h"
31 #include "src/core/SkPicturePriv.h"
32 #include "src/core/SkRectPriv.h"
33 #include "tests/Test.h"
34 
35 #include <memory>
36 
37 class SkRRect;
38 class SkRegion;
39 template <typename T> class SkTDArray;
40 
41 
make_bm(SkBitmap * bm,int w,int h,SkColor color,bool immutable)42 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
43     bm->allocN32Pixels(w, h);
44     bm->eraseColor(color);
45     if (immutable) {
46         bm->setImmutable();
47     }
48 }
49 
50 #ifdef SK_DEBUG
51 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
52 // in debug mode, so only run in debug mode.
test_deleting_empty_picture()53 static void test_deleting_empty_picture() {
54     SkPictureRecorder recorder;
55     // Creates an SkPictureRecord
56     recorder.beginRecording(0, 0);
57     // Turns that into an SkPicture
58     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
59     // Ceates a new SkPictureRecord
60     recorder.beginRecording(0, 0);
61 }
62 
63 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
test_serializing_empty_picture()64 static void test_serializing_empty_picture() {
65     SkPictureRecorder recorder;
66     recorder.beginRecording(0, 0);
67     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
68     SkDynamicMemoryWStream stream;
69     picture->serialize(&stream);
70 }
71 #endif
72 
rand_op(SkCanvas * canvas,SkRandom & rand)73 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
74     SkPaint paint;
75     SkRect rect = SkRect::MakeWH(50, 50);
76 
77     SkScalar unit = rand.nextUScalar1();
78     if (unit <= 0.3) {
79 //        SkDebugf("save\n");
80         canvas->save();
81     } else if (unit <= 0.6) {
82 //        SkDebugf("restore\n");
83         canvas->restore();
84     } else if (unit <= 0.9) {
85 //        SkDebugf("clip\n");
86         canvas->clipRect(rect);
87     } else {
88 //        SkDebugf("draw\n");
89         canvas->drawPaint(paint);
90     }
91 }
92 
set_canvas_to_save_count_4(SkCanvas * canvas)93 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
94     canvas->restoreToCount(1);
95     canvas->save();
96     canvas->save();
97     canvas->save();
98 }
99 
100 /**
101  * A canvas that records the number of saves, saveLayers and restores.
102  */
103 class SaveCountingCanvas : public SkCanvas {
104 public:
SaveCountingCanvas(int width,int height)105     SaveCountingCanvas(int width, int height)
106         : INHERITED(width, height)
107         , fSaveCount(0)
108         , fSaveLayerCount(0)
109         , fSaveBehindCount(0)
110         , fRestoreCount(0){
111     }
112 
getSaveLayerStrategy(const SaveLayerRec & rec)113     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
114         ++fSaveLayerCount;
115         return this->INHERITED::getSaveLayerStrategy(rec);
116     }
117 
onDoSaveBehind(const SkRect * subset)118     bool onDoSaveBehind(const SkRect* subset) override {
119         ++fSaveBehindCount;
120         return this->INHERITED::onDoSaveBehind(subset);
121     }
122 
willSave()123     void willSave() override {
124         ++fSaveCount;
125         this->INHERITED::willSave();
126     }
127 
willRestore()128     void willRestore() override {
129         ++fRestoreCount;
130         this->INHERITED::willRestore();
131     }
132 
getSaveCount() const133     unsigned int getSaveCount() const { return fSaveCount; }
getSaveLayerCount() const134     unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
getSaveBehindCount() const135     unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
getRestoreCount() const136     unsigned int getRestoreCount() const { return fRestoreCount; }
137 
138 private:
139     unsigned int fSaveCount;
140     unsigned int fSaveLayerCount;
141     unsigned int fSaveBehindCount;
142     unsigned int fRestoreCount;
143 
144     using INHERITED = SkCanvas;
145 };
146 
check_save_state(skiatest::Reporter * reporter,SkPicture * picture,unsigned int numSaves,unsigned int numSaveLayers,unsigned int numRestores)147 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
148                       unsigned int numSaves, unsigned int numSaveLayers,
149                       unsigned int numRestores) {
150     SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
151                               SkScalarCeilToInt(picture->cullRect().height()));
152 
153     picture->playback(&canvas);
154 
155     // Optimizations may have removed these,
156     // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
157     REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
158     REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
159     REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
160 }
161 
162 // This class exists so SkPicture can friend it and give it access to
163 // the 'partialReplay' method.
164 class SkPictureRecorderReplayTester {
165 public:
Copy(SkPictureRecorder * recorder)166     static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
167         SkPictureRecorder recorder2;
168 
169         SkCanvas* canvas = recorder2.beginRecording(10, 10);
170 
171         recorder->partialReplay(canvas);
172 
173         return recorder2.finishRecordingAsPicture();
174     }
175 };
176 
create_imbalance(SkCanvas * canvas)177 static void create_imbalance(SkCanvas* canvas) {
178     SkRect clipRect = SkRect::MakeWH(2, 2);
179     SkRect drawRect = SkRect::MakeWH(10, 10);
180     canvas->save();
181         canvas->clipRect(clipRect, SkClipOp::kIntersect);
182         canvas->translate(1.0f, 1.0f);
183         SkPaint p;
184         p.setColor(SK_ColorGREEN);
185         canvas->drawRect(drawRect, p);
186     // no restore
187 }
188 
189 // This tests that replaying a potentially unbalanced picture into a canvas
190 // doesn't affect the canvas' save count or matrix/clip state.
check_balance(skiatest::Reporter * reporter,SkPicture * picture)191 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
192     SkBitmap bm;
193     bm.allocN32Pixels(4, 3);
194     SkCanvas canvas(bm);
195 
196     int beforeSaveCount = canvas.getSaveCount();
197 
198     SkMatrix beforeMatrix = canvas.getTotalMatrix();
199 
200     SkRect beforeClip = canvas.getLocalClipBounds();
201 
202     canvas.drawPicture(picture);
203 
204     REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
205     REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
206 
207     SkRect afterClip = canvas.getLocalClipBounds();
208 
209     REPORTER_ASSERT(reporter, afterClip == beforeClip);
210 }
211 
212 // Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay,reporter)213 DEF_TEST(PictureRecorder_replay, reporter) {
214     // check save/saveLayer state
215     {
216         SkPictureRecorder recorder;
217 
218         SkCanvas* canvas = recorder.beginRecording(10, 10);
219 
220         canvas->saveLayer(nullptr, nullptr);
221 
222         sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
223 
224         // The extra save and restore comes from the Copy process.
225         check_save_state(reporter, copy.get(), 2, 1, 3);
226 
227         canvas->saveLayer(nullptr, nullptr);
228 
229         sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
230 
231         check_save_state(reporter, final.get(), 1, 2, 3);
232 
233         // The copy shouldn't pick up any operations added after it was made
234         check_save_state(reporter, copy.get(), 2, 1, 3);
235     }
236 
237     // Recreate the Android partialReplay test case
238     {
239         SkPictureRecorder recorder;
240 
241         SkCanvas* canvas = recorder.beginRecording(4, 3);
242         create_imbalance(canvas);
243 
244         int expectedSaveCount = canvas->getSaveCount();
245 
246         sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
247         check_balance(reporter, copy.get());
248 
249         REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
250 
251         // End the recording of source to test the picture finalization
252         // process isn't complicated by the partialReplay step
253         sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
254     }
255 }
256 
test_unbalanced_save_restores(skiatest::Reporter * reporter)257 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
258     SkCanvas testCanvas(100, 100);
259     set_canvas_to_save_count_4(&testCanvas);
260 
261     REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
262 
263     SkPaint paint;
264     SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
265 
266     SkPictureRecorder recorder;
267 
268     {
269         // Create picture with 2 unbalanced saves
270         SkCanvas* canvas = recorder.beginRecording(100, 100);
271         canvas->save();
272         canvas->translate(10, 10);
273         canvas->drawRect(rect, paint);
274         canvas->save();
275         canvas->translate(10, 10);
276         canvas->drawRect(rect, paint);
277         sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
278 
279         testCanvas.drawPicture(extraSavePicture);
280         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
281     }
282 
283     set_canvas_to_save_count_4(&testCanvas);
284 
285     {
286         // Create picture with 2 unbalanced restores
287         SkCanvas* canvas = recorder.beginRecording(100, 100);
288         canvas->save();
289         canvas->translate(10, 10);
290         canvas->drawRect(rect, paint);
291         canvas->save();
292         canvas->translate(10, 10);
293         canvas->drawRect(rect, paint);
294         canvas->restore();
295         canvas->restore();
296         canvas->restore();
297         canvas->restore();
298         sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
299 
300         testCanvas.drawPicture(extraRestorePicture);
301         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
302     }
303 
304     set_canvas_to_save_count_4(&testCanvas);
305 
306     {
307         SkCanvas* canvas = recorder.beginRecording(100, 100);
308         canvas->translate(10, 10);
309         canvas->drawRect(rect, paint);
310         sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
311 
312         testCanvas.drawPicture(noSavePicture);
313         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
314         REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
315     }
316 }
317 
test_peephole()318 static void test_peephole() {
319     SkRandom rand;
320 
321     SkPictureRecorder recorder;
322 
323     for (int j = 0; j < 100; j++) {
324         SkRandom rand2(rand); // remember the seed
325 
326         SkCanvas* canvas = recorder.beginRecording(100, 100);
327 
328         for (int i = 0; i < 1000; ++i) {
329             rand_op(canvas, rand);
330         }
331         sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
332 
333         rand = rand2;
334     }
335 
336     {
337         SkCanvas* canvas = recorder.beginRecording(100, 100);
338         SkRect rect = SkRect::MakeWH(50, 50);
339 
340         for (int i = 0; i < 100; ++i) {
341             canvas->save();
342         }
343         while (canvas->getSaveCount() > 1) {
344             canvas->clipRect(rect);
345             canvas->restore();
346         }
347         sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
348     }
349 }
350 
test_bad_bitmap(skiatest::Reporter * reporter)351 static void test_bad_bitmap(skiatest::Reporter* reporter) {
352     // missing pixels should return null for image
353     SkBitmap bm;
354     bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
355     auto img = bm.asImage();
356     REPORTER_ASSERT(reporter, !img);
357 
358     // make sure we don't crash on a null image
359     SkPictureRecorder recorder;
360     SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
361     recordingCanvas->drawImage(nullptr, 0, 0);
362     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
363 
364     SkCanvas canvas;
365     canvas.drawPicture(picture);
366 }
367 
test_clip_bound_opt(skiatest::Reporter * reporter)368 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
369     // Test for crbug.com/229011
370     SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
371                                     SkIntToScalar(2), SkIntToScalar(2));
372     SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
373                                     SkIntToScalar(1), SkIntToScalar(1));
374     SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
375                                     SkIntToScalar(1), SkIntToScalar(1));
376 
377     SkPath invPath;
378     invPath.addOval(rect1);
379     invPath.setFillType(SkPathFillType::kInverseEvenOdd);
380     SkPath path;
381     path.addOval(rect2);
382     SkPath path2;
383     path2.addOval(rect3);
384     SkIRect clipBounds;
385     SkPictureRecorder recorder;
386 
387     // Testing conservative-raster-clip that is enabled by PictureRecord
388     {
389         SkCanvas* canvas = recorder.beginRecording(10, 10);
390         canvas->clipPath(invPath);
391         clipBounds = canvas->getDeviceClipBounds();
392         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
393         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
394         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
395         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
396     }
397     {
398         SkCanvas* canvas = recorder.beginRecording(10, 10);
399         canvas->clipPath(path);
400         canvas->clipPath(invPath);
401         clipBounds = canvas->getDeviceClipBounds();
402         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
403         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
404         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
405         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
406     }
407     {
408         SkCanvas* canvas = recorder.beginRecording(10, 10);
409         canvas->clipPath(path, SkClipOp::kDifference);
410         clipBounds = canvas->getDeviceClipBounds();
411         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
412         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
413         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
414         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
415     }
416     {
417         SkCanvas* canvas = recorder.beginRecording(10, 10);
418         canvas->clipPath(path, SkClipOp::kIntersect);
419         canvas->clipPath(path2, SkClipOp::kDifference);
420         clipBounds = canvas->getDeviceClipBounds();
421         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
422         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
423         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
424         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
425     }
426 }
427 
test_cull_rect_reset(skiatest::Reporter * reporter)428 static void test_cull_rect_reset(skiatest::Reporter* reporter) {
429     SkPictureRecorder recorder;
430     SkRect bounds = SkRect::MakeWH(10, 10);
431     SkRTreeFactory factory;
432     SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
433     bounds = SkRect::MakeWH(100, 100);
434     SkPaint paint;
435     canvas->drawRect(bounds, paint);
436     canvas->drawRect(bounds, paint);
437     sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
438     const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
439     REPORTER_ASSERT(reporter, picture);
440 
441     SkRect finalCullRect = picture->cullRect();
442     REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
443     REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
444     REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
445     REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
446 }
447 
448 
449 /**
450  * A canvas that records the number of clip commands.
451  */
452 class ClipCountingCanvas : public SkCanvas {
453 public:
ClipCountingCanvas(int width,int height)454     ClipCountingCanvas(int width, int height)
455         : INHERITED(width, height)
456         , fClipCount(0){
457     }
458 
onClipRect(const SkRect & r,SkClipOp op,ClipEdgeStyle edgeStyle)459     void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
460         fClipCount += 1;
461         this->INHERITED::onClipRect(r, op, edgeStyle);
462     }
463 
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)464     void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
465         fClipCount += 1;
466         this->INHERITED::onClipRRect(rrect, op, edgeStyle);
467     }
468 
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)469     void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
470         fClipCount += 1;
471         this->INHERITED::onClipPath(path, op, edgeStyle);
472     }
473 
onClipRegion(const SkRegion & deviceRgn,SkClipOp op)474     void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
475         fClipCount += 1;
476         this->INHERITED::onClipRegion(deviceRgn, op);
477     }
478 
getClipCount() const479     unsigned getClipCount() const { return fClipCount; }
480 
481 private:
482     unsigned fClipCount;
483 
484     using INHERITED = SkCanvas;
485 };
486 
test_gen_id(skiatest::Reporter * reporter)487 static void test_gen_id(skiatest::Reporter* reporter) {
488 
489     SkPictureRecorder recorder;
490     recorder.beginRecording(0, 0);
491     sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
492 
493     // Empty pictures should still have a valid ID
494     REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
495 
496     SkCanvas* canvas = recorder.beginRecording(1, 1);
497     canvas->drawColor(SK_ColorWHITE);
498     sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
499     // picture should have a non-zero id after recording
500     REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
501 
502     // both pictures should have different ids
503     REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
504 }
505 
test_typeface(skiatest::Reporter * reporter)506 static void test_typeface(skiatest::Reporter* reporter) {
507     SkPictureRecorder recorder;
508     SkCanvas* canvas = recorder.beginRecording(10, 10);
509     SkFont font(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
510     canvas->drawString("Q", 0, 10, font, SkPaint());
511     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
512     SkDynamicMemoryWStream stream;
513     picture->serialize(&stream);
514 }
515 
DEF_TEST(Picture,reporter)516 DEF_TEST(Picture, reporter) {
517     test_typeface(reporter);
518 #ifdef SK_DEBUG
519     test_deleting_empty_picture();
520     test_serializing_empty_picture();
521 #endif
522     test_bad_bitmap(reporter);
523     test_unbalanced_save_restores(reporter);
524     test_peephole();
525     test_clip_bound_opt(reporter);
526     test_gen_id(reporter);
527     test_cull_rect_reset(reporter);
528 }
529 
draw_bitmaps(const SkBitmap bitmap,SkCanvas * canvas)530 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
531     const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
532     auto img = bitmap.asImage();
533 
534     // Don't care what these record, as long as they're legal.
535     canvas->drawImage(img, 0.0f, 0.0f);
536     canvas->drawImageRect(img, rect, rect, SkSamplingOptions(), nullptr,
537                           SkCanvas::kStrict_SrcRectConstraint);
538     canvas->drawImage(img, 1, 1);   // drawSprite
539 }
540 
test_draw_bitmaps(SkCanvas * canvas)541 static void test_draw_bitmaps(SkCanvas* canvas) {
542     SkBitmap empty;
543     draw_bitmaps(empty, canvas);
544     empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
545     draw_bitmaps(empty, canvas);
546 }
547 
DEF_TEST(Picture_EmptyBitmap,r)548 DEF_TEST(Picture_EmptyBitmap, r) {
549     SkPictureRecorder recorder;
550     test_draw_bitmaps(recorder.beginRecording(10, 10));
551     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
552 }
553 
DEF_TEST(Canvas_EmptyBitmap,r)554 DEF_TEST(Canvas_EmptyBitmap, r) {
555     SkBitmap dst;
556     dst.allocN32Pixels(10, 10);
557     SkCanvas canvas(dst);
558 
559     test_draw_bitmaps(&canvas);
560 }
561 
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore,reporter)562 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
563     // This test is from crbug.com/344987.
564     // The commands are:
565     //   saveLayer with paint that modifies alpha
566     //     drawBitmapRect
567     //     drawBitmapRect
568     //   restore
569     // The bug was that this structure was modified so that:
570     //  - The saveLayer and restore were eliminated
571     //  - The alpha was only applied to the first drawBitmapRectToRect
572 
573     // This test draws blue and red squares inside a 50% transparent
574     // layer.  Both colours should show up muted.
575     // When the bug is present, the red square (the second bitmap)
576     // shows upwith full opacity.
577 
578     SkBitmap blueBM;
579     make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
580     SkBitmap redBM;
581     make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
582     SkPaint semiTransparent;
583     semiTransparent.setAlpha(0x80);
584 
585     SkPictureRecorder recorder;
586     SkCanvas* canvas = recorder.beginRecording(100, 100);
587     canvas->drawColor(0);
588 
589     canvas->saveLayer(nullptr, &semiTransparent);
590     canvas->drawImage(blueBM.asImage(), 25, 25);
591     canvas->drawImage(redBM.asImage(), 50, 50);
592     canvas->restore();
593 
594     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
595 
596     // Now replay the picture back on another canvas
597     // and check a couple of its pixels.
598     SkBitmap replayBM;
599     make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
600     SkCanvas replayCanvas(replayBM);
601     picture->playback(&replayCanvas);
602 
603     // With the bug present, at (55, 55) we would get a fully opaque red
604     // intead of a dark red.
605     REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
606     REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
607 }
608 
609 struct CountingBBH : public SkBBoxHierarchy {
610     mutable int searchCalls;
611 
CountingBBHCountingBBH612     CountingBBH() : searchCalls(0) {}
613 
searchCountingBBH614     void search(const SkRect& query, std::vector<int>* results) const override {
615         this->searchCalls++;
616     }
617 
insertCountingBBH618     void insert(const SkRect[], int) override {}
bytesUsedCountingBBH619     size_t bytesUsed() const override { return 0; }
620 };
621 
622 class SpoonFedBBHFactory : public SkBBHFactory {
623 public:
SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh)624     explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(bbh) {}
operator ()() const625     sk_sp<SkBBoxHierarchy> operator()() const override {
626         return fBBH;
627     }
628 private:
629     sk_sp<SkBBoxHierarchy> fBBH;
630 };
631 
632 // When the canvas clip covers the full picture, we don't need to call the BBH.
DEF_TEST(Picture_SkipBBH,r)633 DEF_TEST(Picture_SkipBBH, r) {
634     SkRect bound = SkRect::MakeWH(320, 240);
635 
636     auto bbh = sk_make_sp<CountingBBH>();
637     SpoonFedBBHFactory factory(bbh);
638 
639     SkPictureRecorder recorder;
640     SkCanvas* c = recorder.beginRecording(bound, &factory);
641     // Record a few ops so we don't hit a small- or empty- picture optimization.
642         c->drawRect(bound, SkPaint());
643         c->drawRect(bound, SkPaint());
644     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
645 
646     SkCanvas big(640, 480), small(300, 200);
647 
648     picture->playback(&big);
649     REPORTER_ASSERT(r, bbh->searchCalls == 0);
650 
651     picture->playback(&small);
652     REPORTER_ASSERT(r, bbh->searchCalls == 1);
653 }
654 
DEF_TEST(Picture_BitmapLeak,r)655 DEF_TEST(Picture_BitmapLeak, r) {
656     SkBitmap mut, immut;
657     mut.allocN32Pixels(300, 200);
658     immut.allocN32Pixels(300, 200);
659     immut.setImmutable();
660     SkASSERT(!mut.isImmutable());
661     SkASSERT(immut.isImmutable());
662 
663     // No one can hold a ref on our pixels yet.
664     REPORTER_ASSERT(r, mut.pixelRef()->unique());
665     REPORTER_ASSERT(r, immut.pixelRef()->unique());
666 
667     sk_sp<SkPicture> pic;
668     {
669         // we want the recorder to go out of scope before our subsequent checks, so we
670         // place it inside local braces.
671         SkPictureRecorder rec;
672         SkCanvas* canvas = rec.beginRecording(1920, 1200);
673             canvas->drawImage(mut.asImage(), 0, 0);
674             canvas->drawImage(immut.asImage(), 800, 600);
675         pic = rec.finishRecordingAsPicture();
676     }
677 
678     // The picture shares the immutable pixels but copies the mutable ones.
679     REPORTER_ASSERT(r, mut.pixelRef()->unique());
680     REPORTER_ASSERT(r, !immut.pixelRef()->unique());
681 
682     // When the picture goes away, it's just our bitmaps holding the refs.
683     pic = nullptr;
684     REPORTER_ASSERT(r, mut.pixelRef()->unique());
685     REPORTER_ASSERT(r, immut.pixelRef()->unique());
686 }
687 
688 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
DEF_TEST(Picture_getRecordingCanvas,r)689 DEF_TEST(Picture_getRecordingCanvas, r) {
690     SkPictureRecorder rec;
691     REPORTER_ASSERT(r, !rec.getRecordingCanvas());
692     for (int i = 0; i < 3; i++) {
693         rec.beginRecording(100, 100);
694         REPORTER_ASSERT(r, rec.getRecordingCanvas());
695         rec.finishRecordingAsPicture();
696         REPORTER_ASSERT(r, !rec.getRecordingCanvas());
697     }
698 }
699 
DEF_TEST(MiniRecorderLeftHanging,r)700 DEF_TEST(MiniRecorderLeftHanging, r) {
701     // Any shader or other ref-counted effect will do just fine here.
702     SkPaint paint;
703     paint.setShader(SkShaders::Color(SK_ColorRED));
704 
705     SkMiniRecorder rec;
706     REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
707     // Don't call rec.detachPicture().  Test succeeds by not asserting or leaking the shader.
708 }
709 
DEF_TEST(Picture_preserveCullRect,r)710 DEF_TEST(Picture_preserveCullRect, r) {
711     SkPictureRecorder recorder;
712 
713     SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
714     c->clear(SK_ColorCYAN);
715 
716     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
717     SkDynamicMemoryWStream wstream;
718     picture->serialize(&wstream);
719 
720     std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
721     sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
722 
723     REPORTER_ASSERT(r, deserializedPicture != nullptr);
724     REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
725     REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
726     REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
727     REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
728 }
729 
730 
731 // If we record bounded ops into a picture with a big cull and calculate the
732 // bounds of those ops, we should trim down the picture cull to the ops' bounds.
733 // If we're not using an SkBBH, we shouldn't change it.
DEF_TEST(Picture_UpdatedCull_1,r)734 DEF_TEST(Picture_UpdatedCull_1, r) {
735     // Testing 1 draw exercises SkMiniPicture.
736     SkRTreeFactory factory;
737     SkPictureRecorder recorder;
738 
739     auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
740     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
741     auto pic = recorder.finishRecordingAsPicture();
742     REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
743 
744     canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
745     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
746     pic = recorder.finishRecordingAsPicture();
747     REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
748 }
DEF_TEST(Picture_UpdatedCull_2,r)749 DEF_TEST(Picture_UpdatedCull_2, r) {
750     // Testing >1 draw exercises SkBigPicture.
751     SkRTreeFactory factory;
752     SkPictureRecorder recorder;
753 
754     auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
755     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
756     canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
757     auto pic = recorder.finishRecordingAsPicture();
758     REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
759 
760     canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
761     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
762     canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
763     pic = recorder.finishRecordingAsPicture();
764     REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
765 }
766 
DEF_TEST(Picture_RecordsFlush,r)767 DEF_TEST(Picture_RecordsFlush, r) {
768     SkPictureRecorder recorder;
769 
770     auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
771     for (int i = 0; i < 10; i++) {
772         canvas->clear(0);
773         for (int j = 0; j < 10; j++) {
774             canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
775         }
776         canvas->flush();
777     }
778 
779     // Did we record the flushes?
780     auto pic = recorder.finishRecordingAsPicture();
781     REPORTER_ASSERT(r, pic->approximateOpCount() == 120);  // 10 clears, 100 draws, 10 flushes
782 
783     // Do we serialize and deserialize flushes?
784     auto skp = pic->serialize();
785     auto back = SkPicture::MakeFromData(skp->data(), skp->size());
786     REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
787 }
788 
DEF_TEST(Placeholder,r)789 DEF_TEST(Placeholder, r) {
790     SkRect cull = { 0,0, 10,20 };
791 
792     // Each placeholder is unique.
793     sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
794                      p2 = SkPicture::MakePlaceholder(cull);
795     REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
796     REPORTER_ASSERT(r, p1->cullRect() == cull);
797     REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
798 
799     // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
800     SkPictureRecorder recorder;
801     SkCanvas* canvas = recorder.beginRecording(cull);
802         canvas->drawPicture(p1);
803         canvas->drawPicture(p2);
804     sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
805     REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
806 
807     // Any upper limit when recursing into nested placeholders is fine as long
808     // as it doesn't overflow an int.
809     REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) >=  2);
810     REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) <= 10);
811 }
812 
DEF_TEST(Picture_empty_serial,reporter)813 DEF_TEST(Picture_empty_serial, reporter) {
814     SkPictureRecorder rec;
815     (void)rec.beginRecording(10, 10);
816     auto pic = rec.finishRecordingAsPicture();
817     REPORTER_ASSERT(reporter, pic);
818 
819     auto data = pic->serialize();
820     REPORTER_ASSERT(reporter, data);
821 
822     auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
823     REPORTER_ASSERT(reporter, pic2);
824 }
825 
826 
DEF_TEST(Picture_drawsNothing,r)827 DEF_TEST(Picture_drawsNothing, r) {
828     // Tests that pic->cullRect().isEmpty() is a good way to test a picture
829     // recorded with an R-tree draws nothing.
830     struct {
831         bool draws_nothing;
832         void (*fn)(SkCanvas*);
833     } cases[] = {
834         {  true, [](SkCanvas* c) {                                                             } },
835         {  true, [](SkCanvas* c) { c->save();                                    c->restore(); } },
836         {  true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5});            c->restore(); } },
837         {  true, [](SkCanvas* c) {            c->clipRect({0,0,5,5});                          } },
838 
839         { false, [](SkCanvas* c) {            c->drawRect({0,0,5,5}, SkPaint{});               } },
840         { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
841         { false, [](SkCanvas* c) {
842             c->drawRect({0,0, 5, 5}, SkPaint{});
843             c->drawRect({5,5,10,10}, SkPaint{});
844         }},
845     };
846 
847     for (const auto& c : cases) {
848         SkPictureRecorder rec;
849         SkRTreeFactory factory;
850         c.fn(rec.beginRecording(10,10, &factory));
851         sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
852 
853         REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
854     }
855 }
856 
DEF_TEST(Picture_emptyNestedPictureBug,r)857 DEF_TEST(Picture_emptyNestedPictureBug, r) {
858     const SkRect bounds = {-5000, -5000, 5000, 5000};
859 
860     SkPictureRecorder recorder;
861     SkRTreeFactory factory;
862 
863     // These three pictures should all draw the same but due to bugs they don't:
864     //
865     //   1) inner has enough content that it is recoreded as an SkBigPicture,
866     //      and all its content falls outside the positive/positive quadrant,
867     //      and it is recorded with an R-tree so we contract the cullRect to those bounds;
868     //
869     //   2) middle wraps inner,
870     //      and it its recorded with an R-tree so we update middle's cullRect to inner's;
871     //
872     //   3) outer wraps inner,
873     //      and notices that middle contains only one op, drawPicture(inner),
874     //      so it plays middle back during recording rather than ref'ing middle,
875     //      querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
876     //      finding nothing to draw.
877     //
878     //  * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
879     {
880         SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
881         canvas->translate(-100,-100);
882         canvas->drawRect({0,0,50,50}, SkPaint{});
883     }
884     sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
885 
886     recorder.beginRecording(bounds, &factory)->drawPicture(inner);
887     sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
888 
889     // This doesn't need &factory to reproduce the bug,
890     // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
891     recorder.beginRecording(bounds, &factory)->drawPicture(middle);
892     sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
893 
894     REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
895     REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
896     REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50}));   // Used to fail.
897 }
898 
DEF_TEST(Picture_fillsBBH,r)899 DEF_TEST(Picture_fillsBBH, r) {
900     // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
901     const SkRect rects[] = {
902         { 0, 0, 20,20},
903         {20,20, 40,40},
904     };
905 
906     for (int n = 0; n <= 2; n++) {
907         SkRTreeFactory factory;
908         SkPictureRecorder rec;
909 
910         sk_sp<SkBBoxHierarchy> bbh = factory();
911 
912         SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
913         for (int i = 0; i < n; i++) {
914             c->drawRect(rects[i], SkPaint{});
915         }
916         sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
917 
918         std::vector<int> results;
919         bbh->search({0,0, 100,100}, &results);
920         REPORTER_ASSERT(r, (int)results.size() == n,
921                         "results.size() == %d, want %d\n", (int)results.size(), n);
922     }
923 }
924 
DEF_TEST(Picture_nested_op_count,r)925 DEF_TEST(Picture_nested_op_count, r) {
926     auto make_pic = [](int n, sk_sp<SkPicture> pic) {
927         SkPictureRecorder rec;
928         SkCanvas* c = rec.beginRecording({0,0, 100,100});
929         for (int i = 0; i < n; i++) {
930             if (pic) {
931                 c->drawPicture(pic);
932             } else {
933                 c->drawRect({0,0, 100,100}, SkPaint{});
934             }
935         }
936         return rec.finishRecordingAsPicture();
937     };
938 
939     auto check = [r](sk_sp<SkPicture> pic, int shallow, int nested) {
940         int s = pic->approximateOpCount(false);
941         int n = pic->approximateOpCount(true);
942         REPORTER_ASSERT(r, s == shallow);
943         REPORTER_ASSERT(r, n == nested);
944     };
945 
946     sk_sp<SkPicture> leaf1 = make_pic(1, nullptr);
947     check(leaf1, 1, 1);
948 
949     sk_sp<SkPicture> leaf10 = make_pic(10, nullptr);
950     check(leaf10, 10, 10);
951 
952     check(make_pic( 1, leaf1),   1,   1);
953     check(make_pic( 1, leaf10),  1,  10);
954     check(make_pic(10, leaf1),  10,  10);
955     check(make_pic(10, leaf10), 10, 100);
956 }
957