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