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