Flutter Engine
The Flutter Engine
PictureTest.cpp
Go to the documentation of this file.
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
13#include "include/core/SkData.h"
14#include "include/core/SkFont.h"
16#include "include/core/SkImage.h" // IWYU pragma: keep
20#include "include/core/SkPath.h"
25#include "include/core/SkRect.h"
32#include "src/base/SkRandom.h"
35#include "src/core/SkRectPriv.h"
36#include "tests/Test.h"
38
39#include <cstddef>
40#include <memory>
41#include <vector>
42
43class SkRRect;
44class SkRegion;
45
46static 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.
57static void test_deleting_empty_picture() {
58 SkPictureRecorder recorder;
59 // Creates an SkPictureRecord
60 recorder.beginRecording(0, 0);
61 // Turns that into an SkPicture
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.
68static void test_serializing_empty_picture() {
69 SkPictureRecorder recorder;
70 recorder.beginRecording(0, 0);
73 picture->serialize(&stream, nullptr); // default SkSerialProcs
74}
75#endif
76
77static void rand_op(SkCanvas* canvas, SkRandom& rand) {
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
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 */
108public:
111 , fSaveCount(0)
112 , fSaveLayerCount(0)
113 , fSaveBehindCount(0)
114 , fRestoreCount(0){
115 }
116
118 ++fSaveLayerCount;
119 return this->INHERITED::getSaveLayerStrategy(rec);
120 }
121
122 bool onDoSaveBehind(const SkRect* subset) override {
123 ++fSaveBehindCount;
124 return this->INHERITED::onDoSaveBehind(subset);
125 }
126
127 void willSave() override {
128 ++fSaveCount;
129 this->INHERITED::willSave();
130 }
131
132 void willRestore() override {
133 ++fRestoreCount;
135 }
136
137 unsigned int getSaveCount() const { return fSaveCount; }
138 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
139 unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
140 unsigned int getRestoreCount() const { return fRestoreCount; }
141
142private:
143 unsigned int fSaveCount;
144 unsigned int fSaveLayerCount;
145 unsigned int fSaveBehindCount;
146 unsigned int fRestoreCount;
147
148 using INHERITED = SkCanvas;
149};
150
152 unsigned int numSaves, unsigned int numSaveLayers,
153 unsigned int numRestores) {
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.
169public:
171 SkPictureRecorder recorder2;
172
173 SkCanvas* canvas = recorder2.beginRecording(10, 10);
174
175 recorder->partialReplay(canvas);
176
177 return recorder2.finishRecordingAsPicture();
178 }
179};
180
181static void create_imbalance(SkCanvas* canvas) {
183 SkRect drawRect = SkRect::MakeWH(10, 10);
184 canvas->save();
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.
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
217DEF_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
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
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
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
258 }
259}
260
262 SkCanvas testCanvas(100, 100);
263 set_canvas_to_save_count_4(&testCanvas);
264
265 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
266
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());
319 }
320}
321
322static 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 }
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 }
352 }
353}
354
356 // missing pixels should return null for image
357 SkBitmap bm;
359 auto img = bm.asImage();
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);
367
368 SkCanvas canvas;
369 canvas.drawPicture(picture);
370}
371
373 // Test for crbug.com/229011
380
381 SkPath invPath;
382 invPath.addOval(rect1);
384 SkPath path;
385 path.addOval(rect2);
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);
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);
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
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);
439 canvas->drawRect(bounds, paint);
440 canvas->drawRect(bounds, paint);
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 */
457public:
460 , fClipCount(0){
461 }
462
463 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
464 fClipCount += 1;
465 this->INHERITED::onClipRect(r, op, edgeStyle);
466 }
467
468 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
469 fClipCount += 1;
470 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
471 }
472
473 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
474 fClipCount += 1;
475 this->INHERITED::onClipPath(path, op, edgeStyle);
476 }
477
478 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
479 fClipCount += 1;
480 this->INHERITED::onClipRegion(deviceRgn, op);
481 }
482
483 unsigned getClipCount() const { return fClipCount; }
484
485private:
486 unsigned fClipCount;
487
488 using INHERITED = SkCanvas;
489};
490
492
493 SkPictureRecorder recorder;
494 recorder.beginRecording(0, 0);
496
497 // Empty pictures should still have a valid ID
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
505
506 // both pictures should have different ids
507 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
508}
509
511 SkPictureRecorder recorder;
512 SkCanvas* canvas = recorder.beginRecording(10, 10);
514 canvas->drawString("Q", 0, 10, font, SkPaint());
517 picture->serialize(&stream, nullptr); // default SkSerialProcs
518}
519
522#ifdef SK_DEBUG
523 test_deleting_empty_picture();
524 test_serializing_empty_picture();
525#endif
532}
533
534static 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,
542 canvas->drawImage(img, 1, 1); // drawSprite
543}
544
545static void test_draw_bitmaps(SkCanvas* canvas) {
547 draw_bitmaps(empty, canvas);
548 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
549 draw_bitmaps(empty, canvas);
550}
551
552DEF_TEST(Picture_EmptyBitmap, r) {
553 SkPictureRecorder recorder;
554 test_draw_bitmaps(recorder.beginRecording(10, 10));
556}
557
558DEF_TEST(Canvas_EmptyBitmap, r) {
560 dst.allocN32Pixels(10, 10);
561 SkCanvas canvas(dst);
562
563 test_draw_bitmaps(&canvas);
564}
565
566DEF_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
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
614 mutable int searchCalls;
615
617
618 void search(const SkRect& query, std::vector<int>* results) const override {
619 this->searchCalls++;
620 }
621
622 void insert(const SkRect[], int) override {}
623 size_t bytesUsed() const override { return 0; }
624};
625
627public:
628 explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(std::move(bbh)) {}
630 return fBBH;
631 }
632private:
634};
635
636// When the canvas clip covers the full picture, we don't need to call the BBH.
637DEF_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());
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
659DEF_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
672 {
673 // we want the recorder to go out of scope before our subsequent checks, so we
674 // place it inside local braces.
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.
693DEF_TEST(Picture_getRecordingCanvas, r) {
696 for (int i = 0; i < 3; i++) {
697 rec.beginRecording(100, 100);
701 }
702}
703
704DEF_TEST(Picture_preserveCullRect, r) {
705 SkPictureRecorder recorder;
706
707 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
709
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.
728DEF_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}
742DEF_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
759DEF_TEST(Placeholder, r) {
760 SkRect cull = { 0,0, 10,20 };
761
762 // Each placeholder is unique.
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);
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
783DEF_TEST(Picture_empty_serial, reporter) {
785 (void)rec.beginRecording(10, 10);
786 auto pic = rec.finishRecordingAsPicture();
788
789 auto data = pic->serialize(); // explicitly testing the default SkSerialProcs
791
792 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
794}
795
796
797DEF_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) {
819 SkRTreeFactory factory;
820 c.fn(rec.beginRecording(10,10, &factory));
822
823 REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
824 }
825}
826
827DEF_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 }
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);
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
869DEF_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;
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 }
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
895DEF_TEST(Picture_nested_op_count, r) {
896 auto make_pic = [](int n, const sk_sp<SkPicture>& pic) {
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}
reporter
Definition: FontMgrTest.cpp:39
static SkPath path2()
static void test_cull_rect_reset(skiatest::Reporter *reporter)
static void test_unbalanced_save_restores(skiatest::Reporter *reporter)
static void set_canvas_to_save_count_4(SkCanvas *canvas)
Definition: PictureTest.cpp:97
static void draw_bitmaps(const SkBitmap &bitmap, SkCanvas *canvas)
void check_save_state(skiatest::Reporter *reporter, SkPicture *picture, unsigned int numSaves, unsigned int numSaveLayers, unsigned int numRestores)
static void make_bm(SkBitmap *bm, int w, int h, SkColor color, bool immutable)
Definition: PictureTest.cpp:46
static void test_typeface(skiatest::Reporter *reporter)
static void rand_op(SkCanvas *canvas, SkRandom &rand)
Definition: PictureTest.cpp:77
static void test_draw_bitmaps(SkCanvas *canvas)
static void test_peephole()
static void create_imbalance(SkCanvas *canvas)
static void check_balance(skiatest::Reporter *reporter, SkPicture *picture)
DEF_TEST(PictureRecorder_replay, reporter)
static void test_gen_id(skiatest::Reporter *reporter)
static void test_bad_bitmap(skiatest::Reporter *reporter)
static void test_clip_bound_opt(skiatest::Reporter *reporter)
#define check(reporter, ref, unref, make, kill)
Definition: RefCntTest.cpp:85
static sk_sp< SkPicture > make_pic(const std::function< void(SkCanvas *)> &drawer)
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkClipOp
Definition: SkClipOp.h:13
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorCYAN
Definition: SkColor.h:143
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SkIntToScalar(x)
Definition: SkScalar.h:57
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:31
static constexpr uint32_t SK_InvalidGenID
Definition: SkTypes.h:192
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
void onClipRegion(const SkRegion &deviceRgn, SkClipOp op) override
void onClipRect(const SkRect &r, SkClipOp op, ClipEdgeStyle edgeStyle) override
unsigned getClipCount() const
void onClipRRect(const SkRRect &rrect, SkClipOp op, ClipEdgeStyle edgeStyle) override
void onClipPath(const SkPath &path, SkClipOp op, ClipEdgeStyle edgeStyle) override
ClipCountingCanvas(int width, int height)
void willRestore() override
bool onDoSaveBehind(const SkRect *subset) override
unsigned int getSaveCount() const
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec &rec) override
unsigned int getSaveLayerCount() const
unsigned int getRestoreCount() const
void willSave() override
SaveCountingCanvas(int width, int height)
unsigned int getSaveBehindCount() const
virtual void search(const SkRect &query, std::vector< int > *results) const =0
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
void setImmutable()
Definition: SkBitmap.cpp:400
SkPixelRef * pixelRef() const
Definition: SkBitmap.h:720
SkColor getColor(int x, int y) const
Definition: SkBitmap.h:874
bool isImmutable() const
Definition: SkBitmap.cpp:396
void allocN32Pixels(int width, int height, bool isOpaque=false)
Definition: SkBitmap.cpp:232
bool setInfo(const SkImageInfo &imageInfo, size_t rowBytes=0)
Definition: SkBitmap.cpp:114
void eraseColor(SkColor4f) const
Definition: SkBitmap.cpp:442
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1361
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
SkRect getLocalClipBounds() const
Definition: SkCanvas.cpp:1586
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec &)
Definition: SkCanvas.h:2270
virtual void onClipRect(const SkRect &rect, SkClipOp op, ClipEdgeStyle edgeStyle)
Definition: SkCanvas.cpp:1370
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition: SkCanvas.h:1542
SaveLayerStrategy
Definition: SkCanvas.h:2263
void clear(SkColor color)
Definition: SkCanvas.h:1199
virtual void willRestore()
Definition: SkCanvas.h:2276
int getSaveCount() const
Definition: SkCanvas.cpp:431
void restoreToCount(int saveCount)
Definition: SkCanvas.cpp:478
SkMatrix getTotalMatrix() const
Definition: SkCanvas.cpp:1629
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1456
SkIRect getDeviceClipBounds() const
Definition: SkCanvas.cpp:1607
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
Definition: SkCanvas.cpp:2333
int save()
Definition: SkCanvas.cpp:447
virtual void onClipRRect(const SkRRect &rrect, SkClipOp op, ClipEdgeStyle edgeStyle)
Definition: SkCanvas.cpp:1449
virtual void onClipPath(const SkPath &path, SkClipOp op, ClipEdgeStyle edgeStyle)
Definition: SkCanvas.cpp:1481
virtual bool onDoSaveBehind(const SkRect *)
Definition: SkCanvas.h:2275
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition: SkCanvas.h:1803
void drawPicture(const SkPicture *picture)
Definition: SkCanvas.h:1961
virtual void willSave()
Definition: SkCanvas.h:2268
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
virtual void onClipRegion(const SkRegion &deviceRgn, SkClipOp op)
Definition: SkCanvas.cpp:1515
std::unique_ptr< SkStreamAsset > detachAsStream()
Definition: SkStream.cpp:876
static constexpr SkFontStyle Italic()
Definition: SkFontStyle.h:72
Definition: SkFont.h:35
bool isIdentity() const
Definition: SkMatrix.h:223
void setAlpha(U8CPU a)
Definition: SkPaint.h:279
Definition: SkPath.h:59
void setFillType(SkPathFillType ft)
Definition: SkPath.h:235
SkPath & addOval(const SkRect &oval, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1106
static const SkBigPicture * AsSkBigPicture(const sk_sp< const SkPicture > &picture)
Definition: SkPicturePriv.h:36
static sk_sp< SkPicture > Copy(SkPictureRecorder *recorder)
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPictureWithCull(const SkRect &cullRect)
SkCanvas * getRecordingCanvas()
sk_sp< SkPicture > finishRecordingAsPicture()
uint32_t uniqueID() const
Definition: SkPicture.h:155
sk_sp< SkData > serialize(const SkSerialProcs *procs=nullptr) const
Definition: SkPicture.cpp:249
virtual SkRect cullRect() const =0
static sk_sp< SkPicture > MakeFromData(const SkData *data, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:160
virtual void playback(SkCanvas *canvas, AbortCallback *callback=nullptr) const =0
static sk_sp< SkPicture > MakePlaceholder(SkRect cull)
Definition: SkPicture.cpp:337
static sk_sp< SkPicture > MakeFromStream(SkStream *stream, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:147
virtual int approximateOpCount(bool nested=false) const =0
SkScalar nextUScalar1()
Definition: SkRandom.h:101
static SkRect MakeLargest()
Definition: SkRectPriv.h:39
bool unique() const
Definition: SkRefCnt.h:50
sk_sp< SkBBoxHierarchy > operator()() const override
SpoonFedBBHFactory(sk_sp< SkBBoxHierarchy > bbh)
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
struct MyStruct s
EMSCRIPTEN_KEEPALIVE void empty()
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
sk_sp< SkTypeface > CreateTestTypeface(const char *name, SkFontStyle style)
Definition: bitmap.py:1
Definition: copy.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
font
Font Metadata and Metrics.
dst
Definition: cp.py:12
const myers::Point & get(const myers::Segment &)
SK_API sk_sp< PrecompileShader > Picture()
SkSamplingOptions(SkFilterMode::kLinear))
Definition: ref_ptr.h:256
SkScalar w
SkScalar h
int32_t height
int32_t width
void search(const SkRect &query, std::vector< int > *results) const override
void insert(const SkRect[], int) override
size_t bytesUsed() const override
Definition: SkRect.h:32
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
int32_t fRight
larger x-axis bounds
Definition: SkRect.h:35
static SkImageInfo MakeN32Premul(int width, int height)
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
constexpr float height() const
Definition: SkRect.h:769
constexpr float right() const
Definition: SkRect.h:748
constexpr float width() const
Definition: SkRect.h:762
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
constexpr float bottom() const
Definition: SkRect.h:755
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63