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