blob: fdec6fa565781572ef7bd9aa405692548df72544 [file] [log] [blame]
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001/*
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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkBBHFactory.h"
9#include "include/core/SkBitmap.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkClipOp.h"
12#include "include/core/SkColor.h"
13#include "include/core/SkData.h"
14#include "include/core/SkFontStyle.h"
15#include "include/core/SkImageInfo.h"
16#include "include/core/SkMatrix.h"
17#include "include/core/SkPaint.h"
18#include "include/core/SkPath.h"
19#include "include/core/SkPictureRecorder.h"
20#include "include/core/SkPixelRef.h"
21#include "include/core/SkRect.h"
22#include "include/core/SkRefCnt.h"
23#include "include/core/SkScalar.h"
24#include "include/core/SkShader.h"
25#include "include/core/SkStream.h"
26#include "include/core/SkTypeface.h"
27#include "include/core/SkTypes.h"
28#include "include/utils/SkRandom.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050029#include "src/core/SkBigPicture.h"
Mike Kleina8ceb772019-05-20 12:45:50 +000030#include "src/core/SkMiniRecorder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "src/core/SkPicturePriv.h"
32#include "src/core/SkRectPriv.h"
33#include "tests/Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000034
Ben Wagnercb3d49c2018-03-14 15:07:43 -040035#include <memory>
36
37class SkRRect;
38class SkRegion;
39template <typename T> class SkTDArray;
40
reed@google.com47b679b2014-05-14 18:58:16 +000041
reed@google.comfe7b1ed2012-11-29 21:00:39 +000042static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000043 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000044 bm->eraseColor(color);
45 if (immutable) {
46 bm->setImmutable();
47 }
48}
49
scroggo@google.comd614c6a2012-09-14 17:26:37 +000050#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070051// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070052// in debug mode, so only run in debug mode.
53static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000054 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000055 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070056 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070057 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -070058 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -070059 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070060 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000061}
62
63// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
64static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000065 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070066 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -070067 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +000068 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +000069 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000070}
71#endif
72
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000073static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +000074 SkPaint paint;
75 SkRect rect = SkRect::MakeWH(50, 50);
76
77 SkScalar unit = rand.nextUScalar1();
78 if (unit <= 0.3) {
79// SkDebugf("save\n");
80 canvas->save();
81 } else if (unit <= 0.6) {
82// SkDebugf("restore\n");
83 canvas->restore();
84 } else if (unit <= 0.9) {
85// SkDebugf("clip\n");
86 canvas->clipRect(rect);
87 } else {
88// SkDebugf("draw\n");
89 canvas->drawPaint(paint);
90 }
91}
92
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +000093static void set_canvas_to_save_count_4(SkCanvas* canvas) {
94 canvas->restoreToCount(1);
95 canvas->save();
96 canvas->save();
97 canvas->save();
98}
99
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000100/**
101 * A canvas that records the number of saves, saveLayers and restores.
102 */
103class SaveCountingCanvas : public SkCanvas {
104public:
105 SaveCountingCanvas(int width, int height)
106 : INHERITED(width, height)
107 , fSaveCount(0)
108 , fSaveLayerCount(0)
Mike Reed148b7fd2018-12-18 17:38:18 -0500109 , fSaveBehindCount(0)
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000110 , fRestoreCount(0){
111 }
112
reed4960eee2015-12-18 07:09:18 -0800113 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000114 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800115 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000116 }
117
Mike Reed148b7fd2018-12-18 17:38:18 -0500118 bool onDoSaveBehind(const SkRect* subset) override {
119 ++fSaveBehindCount;
120 return this->INHERITED::onDoSaveBehind(subset);
121 }
122
mtklein36352bf2015-03-25 18:17:31 -0700123 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000124 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400125 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000126 }
127
mtklein36352bf2015-03-25 18:17:31 -0700128 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000129 ++fRestoreCount;
130 this->INHERITED::willRestore();
131 }
132
133 unsigned int getSaveCount() const { return fSaveCount; }
134 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
Mike Reed148b7fd2018-12-18 17:38:18 -0500135 unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000136 unsigned int getRestoreCount() const { return fRestoreCount; }
137
138private:
139 unsigned int fSaveCount;
140 unsigned int fSaveLayerCount;
Mike Reed148b7fd2018-12-18 17:38:18 -0500141 unsigned int fSaveBehindCount;
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000142 unsigned int fRestoreCount;
143
John Stiles7571f9e2020-09-02 22:42:33 -0400144 using INHERITED = SkCanvas;
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000145};
146
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000147void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000148 unsigned int numSaves, unsigned int numSaveLayers,
149 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700150 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700151 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000152
robertphillipsc5ba71d2014-09-04 08:42:50 -0700153 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000154
mtklein87c41382014-09-08 07:31:18 -0700155 // Optimizations may have removed these,
156 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
157 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
158 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
159 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000160}
161
162// This class exists so SkPicture can friend it and give it access to
163// the 'partialReplay' method.
164class SkPictureRecorderReplayTester {
165public:
reedca2622b2016-03-18 07:25:55 -0700166 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000167 SkPictureRecorder recorder2;
168
robertphillips9f1c2412014-06-09 06:25:34 -0700169 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000170
171 recorder->partialReplay(canvas);
172
reedca2622b2016-03-18 07:25:55 -0700173 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000174 }
175};
176
robertphillips9058d602014-06-10 11:45:46 -0700177static void create_imbalance(SkCanvas* canvas) {
178 SkRect clipRect = SkRect::MakeWH(2, 2);
179 SkRect drawRect = SkRect::MakeWH(10, 10);
180 canvas->save();
Michael Ludwig2f6e2f82021-08-03 13:08:50 -0400181 canvas->clipRect(clipRect, SkClipOp::kIntersect);
robertphillips9058d602014-06-10 11:45:46 -0700182 canvas->translate(1.0f, 1.0f);
183 SkPaint p;
184 p.setColor(SK_ColorGREEN);
185 canvas->drawRect(drawRect, p);
186 // no restore
187}
188
189// This tests that replaying a potentially unbalanced picture into a canvas
190// doesn't affect the canvas' save count or matrix/clip state.
191static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
192 SkBitmap bm;
193 bm.allocN32Pixels(4, 3);
194 SkCanvas canvas(bm);
195
196 int beforeSaveCount = canvas.getSaveCount();
197
198 SkMatrix beforeMatrix = canvas.getTotalMatrix();
199
Mike Reed918e1442017-01-23 11:39:45 -0500200 SkRect beforeClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700201
202 canvas.drawPicture(picture);
203
204 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
205 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
206
Mike Reed918e1442017-01-23 11:39:45 -0500207 SkRect afterClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700208
209 REPORTER_ASSERT(reporter, afterClip == beforeClip);
210}
211
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000212// Test out SkPictureRecorder::partialReplay
213DEF_TEST(PictureRecorder_replay, reporter) {
214 // check save/saveLayer state
215 {
216 SkPictureRecorder recorder;
217
robertphillips9f1c2412014-06-09 06:25:34 -0700218 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000219
halcanary96fcdcc2015-08-27 07:41:13 -0700220 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000221
reedca2622b2016-03-18 07:25:55 -0700222 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000223
224 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700225 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000226
halcanary96fcdcc2015-08-27 07:41:13 -0700227 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000228
reedca2622b2016-03-18 07:25:55 -0700229 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000230
reedca2622b2016-03-18 07:25:55 -0700231 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000232
233 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700234 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000235 }
236
robertphillips9058d602014-06-10 11:45:46 -0700237 // Recreate the Android partialReplay test case
238 {
239 SkPictureRecorder recorder;
240
Mike Reed457c6dd2020-08-21 13:42:01 -0400241 SkCanvas* canvas = recorder.beginRecording(4, 3);
robertphillips9058d602014-06-10 11:45:46 -0700242 create_imbalance(canvas);
243
244 int expectedSaveCount = canvas->getSaveCount();
245
reedca2622b2016-03-18 07:25:55 -0700246 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
247 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700248
249 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
250
251 // End the recording of source to test the picture finalization
252 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700253 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700254 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000255}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000256
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000257static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
258 SkCanvas testCanvas(100, 100);
259 set_canvas_to_save_count_4(&testCanvas);
260
261 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
262
263 SkPaint paint;
264 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
265
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000266 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000267
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000268 {
269 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700270 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000271 canvas->save();
272 canvas->translate(10, 10);
273 canvas->drawRect(rect, paint);
274 canvas->save();
275 canvas->translate(10, 10);
276 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700277 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000278
robertphillips9b14f262014-06-04 05:40:44 -0700279 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000280 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
281 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000282
283 set_canvas_to_save_count_4(&testCanvas);
284
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000285 {
286 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700287 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000288 canvas->save();
289 canvas->translate(10, 10);
290 canvas->drawRect(rect, paint);
291 canvas->save();
292 canvas->translate(10, 10);
293 canvas->drawRect(rect, paint);
294 canvas->restore();
295 canvas->restore();
296 canvas->restore();
297 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700298 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000299
robertphillips9b14f262014-06-04 05:40:44 -0700300 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000301 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
302 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000303
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000304 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000305
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000306 {
robertphillips9f1c2412014-06-09 06:25:34 -0700307 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000308 canvas->translate(10, 10);
309 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700310 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000311
robertphillips9b14f262014-06-04 05:40:44 -0700312 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000313 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
314 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
315 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000316}
317
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000318static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000319 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000320
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000321 SkPictureRecorder recorder;
322
reed@google.com21b519d2012-10-02 17:42:15 +0000323 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000324 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000325
robertphillips9f1c2412014-06-09 06:25:34 -0700326 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000327
328 for (int i = 0; i < 1000; ++i) {
329 rand_op(canvas, rand);
330 }
reedca2622b2016-03-18 07:25:55 -0700331 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000332
333 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000334 }
335
336 {
robertphillips9f1c2412014-06-09 06:25:34 -0700337 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000338 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000339
reed@google.com21b519d2012-10-02 17:42:15 +0000340 for (int i = 0; i < 100; ++i) {
341 canvas->save();
342 }
343 while (canvas->getSaveCount() > 1) {
344 canvas->clipRect(rect);
345 canvas->restore();
346 }
reedca2622b2016-03-18 07:25:55 -0700347 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000348 }
349}
350
Mike Reed993e92d2021-01-28 10:44:58 -0500351static void test_bad_bitmap(skiatest::Reporter* reporter) {
352 // missing pixels should return null for image
scroggo@google.com4b90b112012-12-04 15:08:56 +0000353 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000354 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
Mike Reed993e92d2021-01-28 10:44:58 -0500355 auto img = bm.asImage();
356 REPORTER_ASSERT(reporter, !img);
357
358 // make sure we don't crash on a null image
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000359 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700360 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
Mike Reed993e92d2021-01-28 10:44:58 -0500361 recordingCanvas->drawImage(nullptr, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700362 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000363
364 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700365 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000366}
scroggo@google.com4b90b112012-12-04 15:08:56 +0000367
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000368static void test_clip_bound_opt(skiatest::Reporter* reporter) {
369 // Test for crbug.com/229011
370 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
371 SkIntToScalar(2), SkIntToScalar(2));
372 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
373 SkIntToScalar(1), SkIntToScalar(1));
374 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
375 SkIntToScalar(1), SkIntToScalar(1));
376
377 SkPath invPath;
378 invPath.addOval(rect1);
Mike Reed7d34dc72019-11-26 12:17:17 -0500379 invPath.setFillType(SkPathFillType::kInverseEvenOdd);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000380 SkPath path;
381 path.addOval(rect2);
382 SkPath path2;
383 path2.addOval(rect3);
384 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000385 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700386
387 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000388 {
robertphillips9f1c2412014-06-09 06:25:34 -0700389 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700390 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500391 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000392 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
393 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
394 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
395 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
396 }
397 {
robertphillips9f1c2412014-06-09 06:25:34 -0700398 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700399 canvas->clipPath(path);
400 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500401 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000402 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
403 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
404 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
405 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
406 }
407 {
robertphillips9f1c2412014-06-09 06:25:34 -0700408 SkCanvas* canvas = recorder.beginRecording(10, 10);
Michael Ludwig2f6e2f82021-08-03 13:08:50 -0400409 canvas->clipPath(path, SkClipOp::kDifference);
Mike Reed918e1442017-01-23 11:39:45 -0500410 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000411 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
412 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
413 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
414 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
415 }
416 {
robertphillips9f1c2412014-06-09 06:25:34 -0700417 SkCanvas* canvas = recorder.beginRecording(10, 10);
Michael Ludwig2f6e2f82021-08-03 13:08:50 -0400418 canvas->clipPath(path, SkClipOp::kIntersect);
419 canvas->clipPath(path2, SkClipOp::kDifference);
Mike Reed918e1442017-01-23 11:39:45 -0500420 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000421 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
422 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
423 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
424 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
425 }
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000426}
427
schenneyeeff8bb2015-07-07 14:27:10 -0700428static void test_cull_rect_reset(skiatest::Reporter* reporter) {
429 SkPictureRecorder recorder;
430 SkRect bounds = SkRect::MakeWH(10, 10);
431 SkRTreeFactory factory;
432 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
433 bounds = SkRect::MakeWH(100, 100);
434 SkPaint paint;
435 canvas->drawRect(bounds, paint);
436 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700437 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
Cary Clarkefd99cc2018-06-11 16:25:43 -0400438 const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
schenneyeeff8bb2015-07-07 14:27:10 -0700439 REPORTER_ASSERT(reporter, picture);
440
441 SkRect finalCullRect = picture->cullRect();
442 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
443 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
444 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
445 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
schenneyeeff8bb2015-07-07 14:27:10 -0700446}
447
448
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000449/**
450 * A canvas that records the number of clip commands.
451 */
452class ClipCountingCanvas : public SkCanvas {
453public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000454 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000455 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000456 , fClipCount(0){
457 }
458
Mike Reedc1f77742016-12-09 09:00:50 -0500459 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000460 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000461 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000462 }
463
Mike Reedc1f77742016-12-09 09:00:50 -0500464 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000465 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000466 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000467 }
468
Mike Reedc1f77742016-12-09 09:00:50 -0500469 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000470 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000471 this->INHERITED::onClipPath(path, op, edgeStyle);
472 }
473
Mike Reedc1f77742016-12-09 09:00:50 -0500474 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000475 fClipCount += 1;
476 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000477 }
478
479 unsigned getClipCount() const { return fClipCount; }
480
481private:
482 unsigned fClipCount;
483
John Stiles7571f9e2020-09-02 22:42:33 -0400484 using INHERITED = SkCanvas;
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000485};
486
robertphillips@google.comd5500882014-04-02 23:51:13 +0000487static void test_gen_id(skiatest::Reporter* reporter) {
488
Robert Phillipscfaeec42014-07-13 12:00:50 -0400489 SkPictureRecorder recorder;
490 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700491 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000492
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000493 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400494 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000495
robertphillips9f1c2412014-06-09 06:25:34 -0700496 SkCanvas* canvas = recorder.beginRecording(1, 1);
Mike Reed3661bc92017-02-22 13:21:42 -0500497 canvas->drawColor(SK_ColorWHITE);
reedca2622b2016-03-18 07:25:55 -0700498 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000499 // picture should have a non-zero id after recording
500 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000501
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000502 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400503 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000504}
505
caryclark5ef194c2015-08-31 09:22:38 -0700506static void test_typeface(skiatest::Reporter* reporter) {
507 SkPictureRecorder recorder;
508 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc4745d62019-01-07 09:31:58 -0500509 SkFont font(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
510 canvas->drawString("Q", 0, 10, font, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700511 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700512 SkDynamicMemoryWStream stream;
513 picture->serialize(&stream);
514}
515
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000516DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700517 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000518#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700519 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000520 test_serializing_empty_picture();
521#endif
Mike Reed993e92d2021-01-28 10:44:58 -0500522 test_bad_bitmap(reporter);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000523 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000524 test_peephole();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000525 test_clip_bound_opt(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000526 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700527 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000528}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000529
530static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000531 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
Mike Reed607a3822021-01-24 19:49:21 -0500532 auto img = bitmap.asImage();
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000533
534 // Don't care what these record, as long as they're legal.
Mike Reed607a3822021-01-24 19:49:21 -0500535 canvas->drawImage(img, 0.0f, 0.0f);
536 canvas->drawImageRect(img, rect, rect, SkSamplingOptions(), nullptr,
537 SkCanvas::kStrict_SrcRectConstraint);
538 canvas->drawImage(img, 1, 1); // drawSprite
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000539}
540
541static void test_draw_bitmaps(SkCanvas* canvas) {
542 SkBitmap empty;
543 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000544 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000545 draw_bitmaps(empty, canvas);
546}
547
548DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000549 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700550 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700551 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000552}
553
554DEF_TEST(Canvas_EmptyBitmap, r) {
555 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000556 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000557 SkCanvas canvas(dst);
558
559 test_draw_bitmaps(&canvas);
560}
dneto3f22e8c2014-07-30 15:42:22 -0700561
562DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
563 // This test is from crbug.com/344987.
564 // The commands are:
565 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700566 // drawBitmapRect
567 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700568 // restore
569 // The bug was that this structure was modified so that:
570 // - The saveLayer and restore were eliminated
571 // - The alpha was only applied to the first drawBitmapRectToRect
572
573 // This test draws blue and red squares inside a 50% transparent
574 // layer. Both colours should show up muted.
575 // When the bug is present, the red square (the second bitmap)
576 // shows upwith full opacity.
577
578 SkBitmap blueBM;
579 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
580 SkBitmap redBM;
581 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
582 SkPaint semiTransparent;
583 semiTransparent.setAlpha(0x80);
584
585 SkPictureRecorder recorder;
586 SkCanvas* canvas = recorder.beginRecording(100, 100);
Mike Reed3661bc92017-02-22 13:21:42 -0500587 canvas->drawColor(0);
dneto3f22e8c2014-07-30 15:42:22 -0700588
Ben Wagnera93a14a2017-08-28 10:34:05 -0400589 canvas->saveLayer(nullptr, &semiTransparent);
Mike Reed34a0c972021-01-25 17:49:32 -0500590 canvas->drawImage(blueBM.asImage(), 25, 25);
591 canvas->drawImage(redBM.asImage(), 50, 50);
dneto3f22e8c2014-07-30 15:42:22 -0700592 canvas->restore();
593
reedca2622b2016-03-18 07:25:55 -0700594 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700595
596 // Now replay the picture back on another canvas
597 // and check a couple of its pixels.
598 SkBitmap replayBM;
599 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
600 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700601 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700602
603 // With the bug present, at (55, 55) we would get a fully opaque red
604 // intead of a dark red.
605 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
606 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
607}
mtklein3e8232b2014-08-18 13:39:11 -0700608
Mike Klein508fd322020-02-12 09:45:41 -0600609struct CountingBBH : public SkBBoxHierarchy {
mtklein3e8232b2014-08-18 13:39:11 -0700610 mutable int searchCalls;
611
Mike Kleine1bad062020-02-11 16:16:12 -0600612 CountingBBH() : searchCalls(0) {}
mtklein3e8232b2014-08-18 13:39:11 -0700613
Mike Klein508fd322020-02-12 09:45:41 -0600614 void search(const SkRect& query, std::vector<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700615 this->searchCalls++;
616 }
617
mtklein36352bf2015-03-25 18:17:31 -0700618 void insert(const SkRect[], int) override {}
John Stiles1cf2c8d2020-08-13 22:58:04 -0400619 size_t bytesUsed() const override { return 0; }
mtklein3e8232b2014-08-18 13:39:11 -0700620};
621
622class SpoonFedBBHFactory : public SkBBHFactory {
623public:
Mike Klein7a7ee942020-01-21 10:34:55 -0600624 explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(bbh) {}
625 sk_sp<SkBBoxHierarchy> operator()() const override {
626 return fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700627 }
628private:
Mike Klein7a7ee942020-01-21 10:34:55 -0600629 sk_sp<SkBBoxHierarchy> fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700630};
631
632// When the canvas clip covers the full picture, we don't need to call the BBH.
633DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -0800634 SkRect bound = SkRect::MakeWH(320, 240);
Mike Klein7a7ee942020-01-21 10:34:55 -0600635
Mike Kleine1bad062020-02-11 16:16:12 -0600636 auto bbh = sk_make_sp<CountingBBH>();
Mike Klein7a7ee942020-01-21 10:34:55 -0600637 SpoonFedBBHFactory factory(bbh);
mtklein3e8232b2014-08-18 13:39:11 -0700638
639 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -0700640 SkCanvas* c = recorder.beginRecording(bound, &factory);
641 // Record a few ops so we don't hit a small- or empty- picture optimization.
642 c->drawRect(bound, SkPaint());
643 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700644 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -0700645
646 SkCanvas big(640, 480), small(300, 200);
647
robertphillipsc5ba71d2014-09-04 08:42:50 -0700648 picture->playback(&big);
Mike Klein7a7ee942020-01-21 10:34:55 -0600649 REPORTER_ASSERT(r, bbh->searchCalls == 0);
mtklein3e8232b2014-08-18 13:39:11 -0700650
robertphillipsc5ba71d2014-09-04 08:42:50 -0700651 picture->playback(&small);
Mike Klein7a7ee942020-01-21 10:34:55 -0600652 REPORTER_ASSERT(r, bbh->searchCalls == 1);
mtklein3e8232b2014-08-18 13:39:11 -0700653}
mtkleind72094d2014-08-27 12:12:23 -0700654
655DEF_TEST(Picture_BitmapLeak, r) {
656 SkBitmap mut, immut;
657 mut.allocN32Pixels(300, 200);
658 immut.allocN32Pixels(300, 200);
659 immut.setImmutable();
660 SkASSERT(!mut.isImmutable());
661 SkASSERT(immut.isImmutable());
662
663 // No one can hold a ref on our pixels yet.
664 REPORTER_ASSERT(r, mut.pixelRef()->unique());
665 REPORTER_ASSERT(r, immut.pixelRef()->unique());
666
reedca2622b2016-03-18 07:25:55 -0700667 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -0800668 {
669 // we want the recorder to go out of scope before our subsequent checks, so we
670 // place it inside local braces.
671 SkPictureRecorder rec;
672 SkCanvas* canvas = rec.beginRecording(1920, 1200);
Mike Reed34a0c972021-01-25 17:49:32 -0500673 canvas->drawImage(mut.asImage(), 0, 0);
674 canvas->drawImage(immut.asImage(), 800, 600);
reedca2622b2016-03-18 07:25:55 -0700675 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -0800676 }
mtkleind72094d2014-08-27 12:12:23 -0700677
678 // The picture shares the immutable pixels but copies the mutable ones.
679 REPORTER_ASSERT(r, mut.pixelRef()->unique());
680 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
681
682 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -0700683 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -0700684 REPORTER_ASSERT(r, mut.pixelRef()->unique());
685 REPORTER_ASSERT(r, immut.pixelRef()->unique());
686}
mtkleinfeaadee2015-04-08 11:25:48 -0700687
688// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
689DEF_TEST(Picture_getRecordingCanvas, r) {
690 SkPictureRecorder rec;
691 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
692 for (int i = 0; i < 3; i++) {
693 rec.beginRecording(100, 100);
694 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -0700695 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -0700696 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
697 }
698}
mtklein9db912c2015-05-19 11:11:26 -0700699
Mike Kleina8ceb772019-05-20 12:45:50 +0000700DEF_TEST(MiniRecorderLeftHanging, r) {
701 // Any shader or other ref-counted effect will do just fine here.
702 SkPaint paint;
703 paint.setShader(SkShaders::Color(SK_ColorRED));
704
705 SkMiniRecorder rec;
706 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
707 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
708}
709
fmalita2ecc0002015-07-14 13:12:25 -0700710DEF_TEST(Picture_preserveCullRect, r) {
711 SkPictureRecorder recorder;
712
713 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
714 c->clear(SK_ColorCYAN);
715
reedca2622b2016-03-18 07:25:55 -0700716 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -0700717 SkDynamicMemoryWStream wstream;
718 picture->serialize(&wstream);
719
Ben Wagner145dbcd2016-11-03 14:40:50 -0400720 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
721 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -0700722
mtklein5f939ab2016-03-16 10:28:35 -0700723 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -0700724 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
725 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
726 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
727 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
728}
fmalita796e3652016-05-13 11:40:07 -0700729
Mike Klein26eb16f2017-04-10 09:50:25 -0400730
731// If we record bounded ops into a picture with a big cull and calculate the
732// bounds of those ops, we should trim down the picture cull to the ops' bounds.
733// If we're not using an SkBBH, we shouldn't change it.
734DEF_TEST(Picture_UpdatedCull_1, r) {
Mike Kleina8ceb772019-05-20 12:45:50 +0000735 // Testing 1 draw exercises SkMiniPicture.
Mike Klein26eb16f2017-04-10 09:50:25 -0400736 SkRTreeFactory factory;
737 SkPictureRecorder recorder;
738
Mike Reed274218e2018-01-08 15:05:02 -0500739 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400740 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
741 auto pic = recorder.finishRecordingAsPicture();
742 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
743
Mike Reed274218e2018-01-08 15:05:02 -0500744 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400745 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
746 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500747 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400748}
749DEF_TEST(Picture_UpdatedCull_2, r) {
750 // Testing >1 draw exercises SkBigPicture.
751 SkRTreeFactory factory;
752 SkPictureRecorder recorder;
753
Mike Reed274218e2018-01-08 15:05:02 -0500754 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400755 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
756 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
757 auto pic = recorder.finishRecordingAsPicture();
758 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
759
Mike Reed274218e2018-01-08 15:05:02 -0500760 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400761 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
762 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
763 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500764 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400765}
Mike Klein7cc49d62017-08-14 10:39:28 -0400766
Mike Klein88d90712018-01-27 17:30:04 +0000767DEF_TEST(Picture_RecordsFlush, r) {
768 SkPictureRecorder recorder;
769
770 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
771 for (int i = 0; i < 10; i++) {
772 canvas->clear(0);
773 for (int j = 0; j < 10; j++) {
774 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
775 }
776 canvas->flush();
777 }
778
779 // Did we record the flushes?
780 auto pic = recorder.finishRecordingAsPicture();
781 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
782
783 // Do we serialize and deserialize flushes?
784 auto skp = pic->serialize();
785 auto back = SkPicture::MakeFromData(skp->data(), skp->size());
786 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
787}
Mike Kleinfbe66202018-01-26 09:49:48 -0500788
789DEF_TEST(Placeholder, r) {
790 SkRect cull = { 0,0, 10,20 };
791
792 // Each placeholder is unique.
793 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
794 p2 = SkPicture::MakePlaceholder(cull);
795 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
796 REPORTER_ASSERT(r, p1->cullRect() == cull);
797 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
798
799 // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
800 SkPictureRecorder recorder;
801 SkCanvas* canvas = recorder.beginRecording(cull);
802 canvas->drawPicture(p1);
803 canvas->drawPicture(p2);
804 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
805 REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
Mike Kleinf647dc72020-08-19 13:55:05 -0500806
807 // Any upper limit when recursing into nested placeholders is fine as long
808 // as it doesn't overflow an int.
809 REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) >= 2);
810 REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) <= 10);
Mike Kleinfbe66202018-01-26 09:49:48 -0500811}
Mike Reed40d82972018-02-01 14:45:50 -0500812
813DEF_TEST(Picture_empty_serial, reporter) {
814 SkPictureRecorder rec;
815 (void)rec.beginRecording(10, 10);
816 auto pic = rec.finishRecordingAsPicture();
817 REPORTER_ASSERT(reporter, pic);
818
819 auto data = pic->serialize();
820 REPORTER_ASSERT(reporter, data);
821
822 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
823 REPORTER_ASSERT(reporter, pic2);
824}
825
Mike Klein6d20d992019-09-12 12:51:27 -0500826
827DEF_TEST(Picture_drawsNothing, r) {
828 // Tests that pic->cullRect().isEmpty() is a good way to test a picture
829 // recorded with an R-tree draws nothing.
830 struct {
831 bool draws_nothing;
832 void (*fn)(SkCanvas*);
833 } cases[] = {
834 { true, [](SkCanvas* c) { } },
835 { true, [](SkCanvas* c) { c->save(); c->restore(); } },
836 { true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5}); c->restore(); } },
837 { true, [](SkCanvas* c) { c->clipRect({0,0,5,5}); } },
838
839 { false, [](SkCanvas* c) { c->drawRect({0,0,5,5}, SkPaint{}); } },
840 { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
841 { false, [](SkCanvas* c) {
842 c->drawRect({0,0, 5, 5}, SkPaint{});
843 c->drawRect({5,5,10,10}, SkPaint{});
844 }},
845 };
846
847 for (const auto& c : cases) {
848 SkPictureRecorder rec;
849 SkRTreeFactory factory;
850 c.fn(rec.beginRecording(10,10, &factory));
851 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
852
853 REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
854 }
855}
Mike Kleinedf2d722019-10-02 10:53:39 -0500856
857DEF_TEST(Picture_emptyNestedPictureBug, r) {
858 const SkRect bounds = {-5000, -5000, 5000, 5000};
859
860 SkPictureRecorder recorder;
861 SkRTreeFactory factory;
862
863 // These three pictures should all draw the same but due to bugs they don't:
864 //
865 // 1) inner has enough content that it is recoreded as an SkBigPicture,
866 // and all its content falls outside the positive/positive quadrant,
867 // and it is recorded with an R-tree so we contract the cullRect to those bounds;
868 //
869 // 2) middle wraps inner,
870 // and it its recorded with an R-tree so we update middle's cullRect to inner's;
871 //
872 // 3) outer wraps inner,
873 // and notices that middle contains only one op, drawPicture(inner),
874 // so it plays middle back during recording rather than ref'ing middle,
875 // querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
876 // finding nothing to draw.
877 //
878 // * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
879 {
880 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
881 canvas->translate(-100,-100);
882 canvas->drawRect({0,0,50,50}, SkPaint{});
883 }
884 sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
885
886 recorder.beginRecording(bounds, &factory)->drawPicture(inner);
887 sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
888
889 // This doesn't need &factory to reproduce the bug,
890 // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
891 recorder.beginRecording(bounds, &factory)->drawPicture(middle);
892 sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
893
894 REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
895 REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
896 REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50})); // Used to fail.
897}
Mike Klein196e9fb2020-01-21 10:48:46 -0600898
899DEF_TEST(Picture_fillsBBH, r) {
900 // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
901 const SkRect rects[] = {
902 { 0, 0, 20,20},
903 {20,20, 40,40},
904 };
905
906 for (int n = 0; n <= 2; n++) {
907 SkRTreeFactory factory;
908 SkPictureRecorder rec;
909
910 sk_sp<SkBBoxHierarchy> bbh = factory();
911
912 SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
913 for (int i = 0; i < n; i++) {
914 c->drawRect(rects[i], SkPaint{});
915 }
916 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
917
Mike Klein508fd322020-02-12 09:45:41 -0600918 std::vector<int> results;
919 bbh->search({0,0, 100,100}, &results);
920 REPORTER_ASSERT(r, (int)results.size() == n,
921 "results.size() == %d, want %d\n", (int)results.size(), n);
Mike Klein196e9fb2020-01-21 10:48:46 -0600922 }
923}
Mike Reed18305c32020-08-17 17:12:13 -0400924
925DEF_TEST(Picture_nested_op_count, r) {
926 auto make_pic = [](int n, sk_sp<SkPicture> pic) {
927 SkPictureRecorder rec;
928 SkCanvas* c = rec.beginRecording({0,0, 100,100});
929 for (int i = 0; i < n; i++) {
930 if (pic) {
931 c->drawPicture(pic);
932 } else {
933 c->drawRect({0,0, 100,100}, SkPaint{});
934 }
935 }
936 return rec.finishRecordingAsPicture();
937 };
938
939 auto check = [r](sk_sp<SkPicture> pic, int shallow, int nested) {
940 int s = pic->approximateOpCount(false);
941 int n = pic->approximateOpCount(true);
942 REPORTER_ASSERT(r, s == shallow);
943 REPORTER_ASSERT(r, n == nested);
944 };
945
946 sk_sp<SkPicture> leaf1 = make_pic(1, nullptr);
947 check(leaf1, 1, 1);
948
949 sk_sp<SkPicture> leaf10 = make_pic(10, nullptr);
950 check(leaf10, 10, 10);
951
952 check(make_pic( 1, leaf1), 1, 1);
953 check(make_pic( 1, leaf10), 1, 10);
954 check(make_pic(10, leaf1), 10, 10);
955 check(make_pic(10, leaf10), 10, 100);
956}