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