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