blob: 8274a3be9d42affa46a2d2ad429e5b6af244fd60 [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"
29#include "src/core/SkBBoxHierarchy.h"
30#include "src/core/SkBigPicture.h"
31#include "src/core/SkClipOpPriv.h"
Mike Kleina8ceb772019-05-20 12:45:50 +000032#include "src/core/SkMiniRecorder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050033#include "src/core/SkPicturePriv.h"
34#include "src/core/SkRectPriv.h"
35#include "tests/Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000036
Ben Wagnercb3d49c2018-03-14 15:07:43 -040037#include <memory>
38
39class SkRRect;
40class SkRegion;
41template <typename T> class SkTDArray;
42
reed@google.com47b679b2014-05-14 18:58:16 +000043
reed@google.comfe7b1ed2012-11-29 21:00:39 +000044static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000045 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000046 bm->eraseColor(color);
47 if (immutable) {
48 bm->setImmutable();
49 }
50}
51
scroggo@google.comd614c6a2012-09-14 17:26:37 +000052#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070053// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070054// in debug mode, so only run in debug mode.
55static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000056 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000057 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070058 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070059 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -070060 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -070061 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070062 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000063}
64
65// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
66static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000067 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070068 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -070069 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +000070 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +000071 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000072}
73#endif
74
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000075static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +000076 SkPaint paint;
77 SkRect rect = SkRect::MakeWH(50, 50);
78
79 SkScalar unit = rand.nextUScalar1();
80 if (unit <= 0.3) {
81// SkDebugf("save\n");
82 canvas->save();
83 } else if (unit <= 0.6) {
84// SkDebugf("restore\n");
85 canvas->restore();
86 } else if (unit <= 0.9) {
87// SkDebugf("clip\n");
88 canvas->clipRect(rect);
89 } else {
90// SkDebugf("draw\n");
91 canvas->drawPaint(paint);
92 }
93}
94
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +000095static void set_canvas_to_save_count_4(SkCanvas* canvas) {
96 canvas->restoreToCount(1);
97 canvas->save();
98 canvas->save();
99 canvas->save();
100}
101
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000102/**
103 * A canvas that records the number of saves, saveLayers and restores.
104 */
105class SaveCountingCanvas : public SkCanvas {
106public:
107 SaveCountingCanvas(int width, int height)
108 : INHERITED(width, height)
109 , fSaveCount(0)
110 , fSaveLayerCount(0)
Mike Reed148b7fd2018-12-18 17:38:18 -0500111 , fSaveBehindCount(0)
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000112 , fRestoreCount(0){
113 }
114
reed4960eee2015-12-18 07:09:18 -0800115 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000116 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800117 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000118 }
119
Mike Reed148b7fd2018-12-18 17:38:18 -0500120 bool onDoSaveBehind(const SkRect* subset) override {
121 ++fSaveBehindCount;
122 return this->INHERITED::onDoSaveBehind(subset);
123 }
124
mtklein36352bf2015-03-25 18:17:31 -0700125 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000126 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400127 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000128 }
129
mtklein36352bf2015-03-25 18:17:31 -0700130 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000131 ++fRestoreCount;
132 this->INHERITED::willRestore();
133 }
134
135 unsigned int getSaveCount() const { return fSaveCount; }
136 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
Mike Reed148b7fd2018-12-18 17:38:18 -0500137 unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000138 unsigned int getRestoreCount() const { return fRestoreCount; }
139
140private:
141 unsigned int fSaveCount;
142 unsigned int fSaveLayerCount;
Mike Reed148b7fd2018-12-18 17:38:18 -0500143 unsigned int fSaveBehindCount;
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000144 unsigned int fRestoreCount;
145
146 typedef SkCanvas INHERITED;
147};
148
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000149void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000150 unsigned int numSaves, unsigned int numSaveLayers,
151 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700152 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700153 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000154
robertphillipsc5ba71d2014-09-04 08:42:50 -0700155 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000156
mtklein87c41382014-09-08 07:31:18 -0700157 // Optimizations may have removed these,
158 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
159 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
160 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
161 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000162}
163
164// This class exists so SkPicture can friend it and give it access to
165// the 'partialReplay' method.
166class SkPictureRecorderReplayTester {
167public:
reedca2622b2016-03-18 07:25:55 -0700168 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000169 SkPictureRecorder recorder2;
170
robertphillips9f1c2412014-06-09 06:25:34 -0700171 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000172
173 recorder->partialReplay(canvas);
174
reedca2622b2016-03-18 07:25:55 -0700175 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000176 }
177};
178
robertphillips9058d602014-06-10 11:45:46 -0700179static void create_imbalance(SkCanvas* canvas) {
180 SkRect clipRect = SkRect::MakeWH(2, 2);
181 SkRect drawRect = SkRect::MakeWH(10, 10);
182 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500183 canvas->clipRect(clipRect, kReplace_SkClipOp);
robertphillips9058d602014-06-10 11:45:46 -0700184 canvas->translate(1.0f, 1.0f);
185 SkPaint p;
186 p.setColor(SK_ColorGREEN);
187 canvas->drawRect(drawRect, p);
188 // no restore
189}
190
191// This tests that replaying a potentially unbalanced picture into a canvas
192// doesn't affect the canvas' save count or matrix/clip state.
193static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
194 SkBitmap bm;
195 bm.allocN32Pixels(4, 3);
196 SkCanvas canvas(bm);
197
198 int beforeSaveCount = canvas.getSaveCount();
199
200 SkMatrix beforeMatrix = canvas.getTotalMatrix();
201
Mike Reed918e1442017-01-23 11:39:45 -0500202 SkRect beforeClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700203
204 canvas.drawPicture(picture);
205
206 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
207 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
208
Mike Reed918e1442017-01-23 11:39:45 -0500209 SkRect afterClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700210
211 REPORTER_ASSERT(reporter, afterClip == beforeClip);
212}
213
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000214// Test out SkPictureRecorder::partialReplay
215DEF_TEST(PictureRecorder_replay, reporter) {
216 // check save/saveLayer state
217 {
218 SkPictureRecorder recorder;
219
robertphillips9f1c2412014-06-09 06:25:34 -0700220 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000221
halcanary96fcdcc2015-08-27 07:41:13 -0700222 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000223
reedca2622b2016-03-18 07:25:55 -0700224 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000225
226 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700227 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000228
halcanary96fcdcc2015-08-27 07:41:13 -0700229 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000230
reedca2622b2016-03-18 07:25:55 -0700231 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000232
reedca2622b2016-03-18 07:25:55 -0700233 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000234
235 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700236 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000237 }
238
robertphillips9058d602014-06-10 11:45:46 -0700239 // Recreate the Android partialReplay test case
240 {
241 SkPictureRecorder recorder;
242
halcanary96fcdcc2015-08-27 07:41:13 -0700243 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700244 create_imbalance(canvas);
245
246 int expectedSaveCount = canvas->getSaveCount();
247
reedca2622b2016-03-18 07:25:55 -0700248 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
249 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700250
251 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
252
253 // End the recording of source to test the picture finalization
254 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700255 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700256 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000257}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000258
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000259static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
260 SkCanvas testCanvas(100, 100);
261 set_canvas_to_save_count_4(&testCanvas);
262
263 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
264
265 SkPaint paint;
266 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
267
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000268 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000269
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000270 {
271 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700272 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000273 canvas->save();
274 canvas->translate(10, 10);
275 canvas->drawRect(rect, paint);
276 canvas->save();
277 canvas->translate(10, 10);
278 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700279 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000280
robertphillips9b14f262014-06-04 05:40:44 -0700281 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000282 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
283 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000284
285 set_canvas_to_save_count_4(&testCanvas);
286
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000287 {
288 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700289 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000290 canvas->save();
291 canvas->translate(10, 10);
292 canvas->drawRect(rect, paint);
293 canvas->save();
294 canvas->translate(10, 10);
295 canvas->drawRect(rect, paint);
296 canvas->restore();
297 canvas->restore();
298 canvas->restore();
299 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700300 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000301
robertphillips9b14f262014-06-04 05:40:44 -0700302 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000303 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
304 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000305
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000306 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000307
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000308 {
robertphillips9f1c2412014-06-09 06:25:34 -0700309 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000310 canvas->translate(10, 10);
311 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700312 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000313
robertphillips9b14f262014-06-04 05:40:44 -0700314 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000315 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
316 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
317 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000318}
319
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000320static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000321 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000322
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000323 SkPictureRecorder recorder;
324
reed@google.com21b519d2012-10-02 17:42:15 +0000325 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000326 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000327
robertphillips9f1c2412014-06-09 06:25:34 -0700328 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000329
330 for (int i = 0; i < 1000; ++i) {
331 rand_op(canvas, rand);
332 }
reedca2622b2016-03-18 07:25:55 -0700333 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000334
335 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000336 }
337
338 {
robertphillips9f1c2412014-06-09 06:25:34 -0700339 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000340 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000341
reed@google.com21b519d2012-10-02 17:42:15 +0000342 for (int i = 0; i < 100; ++i) {
343 canvas->save();
344 }
345 while (canvas->getSaveCount() > 1) {
346 canvas->clipRect(rect);
347 canvas->restore();
348 }
reedca2622b2016-03-18 07:25:55 -0700349 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000350 }
351}
352
scroggo@google.com4b90b112012-12-04 15:08:56 +0000353#ifndef SK_DEBUG
354// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
355// should never do this.
356static void test_bad_bitmap() {
357 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
358 // fail.
359 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000360 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000361 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700362 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000363 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700364 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000365
366 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700367 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000368}
369#endif
370
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000371static void test_clip_bound_opt(skiatest::Reporter* reporter) {
372 // Test for crbug.com/229011
373 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
374 SkIntToScalar(2), SkIntToScalar(2));
375 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
376 SkIntToScalar(1), SkIntToScalar(1));
377 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
378 SkIntToScalar(1), SkIntToScalar(1));
379
380 SkPath invPath;
381 invPath.addOval(rect1);
Mike Reed7d34dc72019-11-26 12:17:17 -0500382 invPath.setFillType(SkPathFillType::kInverseEvenOdd);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000383 SkPath path;
384 path.addOval(rect2);
385 SkPath path2;
386 path2.addOval(rect3);
387 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000388 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700389
390 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000391 {
robertphillips9f1c2412014-06-09 06:25:34 -0700392 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700393 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500394 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000395 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
396 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
397 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
398 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
399 }
400 {
robertphillips9f1c2412014-06-09 06:25:34 -0700401 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700402 canvas->clipPath(path);
403 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500404 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000405 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
406 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
407 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
408 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
409 }
410 {
robertphillips9f1c2412014-06-09 06:25:34 -0700411 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700412 canvas->clipPath(path);
Mike Reedc1f77742016-12-09 09:00:50 -0500413 canvas->clipPath(invPath, kUnion_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500414 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000415 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 {
robertphillips9f1c2412014-06-09 06:25:34 -0700421 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500422 canvas->clipPath(path, kDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500423 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000424 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
425 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
426 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
427 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
428 }
429 {
robertphillips9f1c2412014-06-09 06:25:34 -0700430 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500431 canvas->clipPath(path, kReverseDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500432 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000433 // True clip is actually empty in this case, but the best
434 // determination we can make using only bounds as input is that the
435 // clip is included in the bounds of 'path'.
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000436 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
437 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
438 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
439 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
440 }
441 {
robertphillips9f1c2412014-06-09 06:25:34 -0700442 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500443 canvas->clipPath(path, kIntersect_SkClipOp);
444 canvas->clipPath(path2, kXOR_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500445 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000446 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
447 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
448 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
449 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
450 }
451}
452
schenneyeeff8bb2015-07-07 14:27:10 -0700453static void test_cull_rect_reset(skiatest::Reporter* reporter) {
454 SkPictureRecorder recorder;
455 SkRect bounds = SkRect::MakeWH(10, 10);
456 SkRTreeFactory factory;
457 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
458 bounds = SkRect::MakeWH(100, 100);
459 SkPaint paint;
460 canvas->drawRect(bounds, paint);
461 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700462 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
Cary Clarkefd99cc2018-06-11 16:25:43 -0400463 const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
schenneyeeff8bb2015-07-07 14:27:10 -0700464 REPORTER_ASSERT(reporter, picture);
465
466 SkRect finalCullRect = picture->cullRect();
467 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
468 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
469 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
470 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
471
Mike Klein1feceb32020-01-21 11:16:37 -0600472 auto pictureBBH = (const SkBBoxHierarchy_Base*)picture->bbh();
schenneyeeff8bb2015-07-07 14:27:10 -0700473 SkRect bbhCullRect = pictureBBH->getRootBound();
474 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
475 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
476 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
477 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
478}
479
480
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000481/**
482 * A canvas that records the number of clip commands.
483 */
484class ClipCountingCanvas : public SkCanvas {
485public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000486 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000487 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000488 , fClipCount(0){
489 }
490
Mike Reedc1f77742016-12-09 09:00:50 -0500491 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000492 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000493 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000494 }
495
Mike Reedc1f77742016-12-09 09:00:50 -0500496 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000497 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000498 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000499 }
500
Mike Reedc1f77742016-12-09 09:00:50 -0500501 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000502 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000503 this->INHERITED::onClipPath(path, op, edgeStyle);
504 }
505
Mike Reedc1f77742016-12-09 09:00:50 -0500506 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000507 fClipCount += 1;
508 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000509 }
510
511 unsigned getClipCount() const { return fClipCount; }
512
513private:
514 unsigned fClipCount;
515
516 typedef SkCanvas INHERITED;
517};
518
519static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000520 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700521 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000522
Mike Reedc1f77742016-12-09 09:00:50 -0500523 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000524 // The following expanding clip should not be skipped.
Mike Reedc1f77742016-12-09 09:00:50 -0500525 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000526 // Draw something so the optimizer doesn't just fold the world.
527 SkPaint p;
528 p.setColor(SK_ColorBLUE);
529 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700530 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000531
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000532 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700533 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000534
535 // Both clips should be present on playback.
536 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
537}
538
robertphillips@google.comd5500882014-04-02 23:51:13 +0000539static void test_gen_id(skiatest::Reporter* reporter) {
540
Robert Phillipscfaeec42014-07-13 12:00:50 -0400541 SkPictureRecorder recorder;
542 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700543 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000544
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000545 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400546 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000547
robertphillips9f1c2412014-06-09 06:25:34 -0700548 SkCanvas* canvas = recorder.beginRecording(1, 1);
Mike Reed3661bc92017-02-22 13:21:42 -0500549 canvas->drawColor(SK_ColorWHITE);
reedca2622b2016-03-18 07:25:55 -0700550 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000551 // picture should have a non-zero id after recording
552 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000553
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000554 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400555 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000556}
557
caryclark5ef194c2015-08-31 09:22:38 -0700558static void test_typeface(skiatest::Reporter* reporter) {
559 SkPictureRecorder recorder;
560 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc4745d62019-01-07 09:31:58 -0500561 SkFont font(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
562 canvas->drawString("Q", 0, 10, font, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700563 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700564 SkDynamicMemoryWStream stream;
565 picture->serialize(&stream);
566}
567
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000568DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700569 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000570#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700571 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000572 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000573#else
574 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000575#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000576 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000577 test_peephole();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000578 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000579 test_clip_expansion(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000580 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700581 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000582}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000583
584static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
585 const SkPaint paint;
586 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
587 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700588 int divs[] = { 2, 3 };
589 SkCanvas::Lattice lattice;
590 lattice.fXCount = lattice.fYCount = 2;
591 lattice.fXDivs = lattice.fYDivs = divs;
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000592
593 // Don't care what these record, as long as they're legal.
594 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -0700595 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000596 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -0800597 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -0700598 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000599}
600
601static void test_draw_bitmaps(SkCanvas* canvas) {
602 SkBitmap empty;
603 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000604 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000605 draw_bitmaps(empty, canvas);
606}
607
608DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000609 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700610 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700611 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000612}
613
614DEF_TEST(Canvas_EmptyBitmap, r) {
615 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000616 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000617 SkCanvas canvas(dst);
618
619 test_draw_bitmaps(&canvas);
620}
dneto3f22e8c2014-07-30 15:42:22 -0700621
622DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
623 // This test is from crbug.com/344987.
624 // The commands are:
625 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700626 // drawBitmapRect
627 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700628 // restore
629 // The bug was that this structure was modified so that:
630 // - The saveLayer and restore were eliminated
631 // - The alpha was only applied to the first drawBitmapRectToRect
632
633 // This test draws blue and red squares inside a 50% transparent
634 // layer. Both colours should show up muted.
635 // When the bug is present, the red square (the second bitmap)
636 // shows upwith full opacity.
637
638 SkBitmap blueBM;
639 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
640 SkBitmap redBM;
641 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
642 SkPaint semiTransparent;
643 semiTransparent.setAlpha(0x80);
644
645 SkPictureRecorder recorder;
646 SkCanvas* canvas = recorder.beginRecording(100, 100);
Mike Reed3661bc92017-02-22 13:21:42 -0500647 canvas->drawColor(0);
dneto3f22e8c2014-07-30 15:42:22 -0700648
Ben Wagnera93a14a2017-08-28 10:34:05 -0400649 canvas->saveLayer(nullptr, &semiTransparent);
dneto3f22e8c2014-07-30 15:42:22 -0700650 canvas->drawBitmap(blueBM, 25, 25);
651 canvas->drawBitmap(redBM, 50, 50);
652 canvas->restore();
653
reedca2622b2016-03-18 07:25:55 -0700654 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700655
656 // Now replay the picture back on another canvas
657 // and check a couple of its pixels.
658 SkBitmap replayBM;
659 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
660 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700661 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700662
663 // With the bug present, at (55, 55) we would get a fully opaque red
664 // intead of a dark red.
665 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
666 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
667}
mtklein3e8232b2014-08-18 13:39:11 -0700668
Mike Klein1feceb32020-01-21 11:16:37 -0600669struct CountingBBH : public SkBBoxHierarchy_Base {
mtklein3e8232b2014-08-18 13:39:11 -0700670 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -0800671 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -0700672
schenney23d85932015-03-06 16:20:28 -0800673 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -0700674
mtkleinc6ad06a2015-08-19 09:51:00 -0700675 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700676 this->searchCalls++;
677 }
678
mtklein36352bf2015-03-25 18:17:31 -0700679 void insert(const SkRect[], int) override {}
680 virtual size_t bytesUsed() const override { return 0; }
681 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -0700682};
683
684class SpoonFedBBHFactory : public SkBBHFactory {
685public:
Mike Klein7a7ee942020-01-21 10:34:55 -0600686 explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(bbh) {}
687 sk_sp<SkBBoxHierarchy> operator()() const override {
688 return fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700689 }
690private:
Mike Klein7a7ee942020-01-21 10:34:55 -0600691 sk_sp<SkBBoxHierarchy> fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700692};
693
694// When the canvas clip covers the full picture, we don't need to call the BBH.
695DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -0800696 SkRect bound = SkRect::MakeWH(320, 240);
Mike Klein7a7ee942020-01-21 10:34:55 -0600697
698 auto bbh = sk_make_sp<CountingBBH>(bound);
699 SpoonFedBBHFactory factory(bbh);
mtklein3e8232b2014-08-18 13:39:11 -0700700
701 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -0700702 SkCanvas* c = recorder.beginRecording(bound, &factory);
703 // Record a few ops so we don't hit a small- or empty- picture optimization.
704 c->drawRect(bound, SkPaint());
705 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700706 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -0700707
708 SkCanvas big(640, 480), small(300, 200);
709
robertphillipsc5ba71d2014-09-04 08:42:50 -0700710 picture->playback(&big);
Mike Klein7a7ee942020-01-21 10:34:55 -0600711 REPORTER_ASSERT(r, bbh->searchCalls == 0);
mtklein3e8232b2014-08-18 13:39:11 -0700712
robertphillipsc5ba71d2014-09-04 08:42:50 -0700713 picture->playback(&small);
Mike Klein7a7ee942020-01-21 10:34:55 -0600714 REPORTER_ASSERT(r, bbh->searchCalls == 1);
mtklein3e8232b2014-08-18 13:39:11 -0700715}
mtkleind72094d2014-08-27 12:12:23 -0700716
717DEF_TEST(Picture_BitmapLeak, r) {
718 SkBitmap mut, immut;
719 mut.allocN32Pixels(300, 200);
720 immut.allocN32Pixels(300, 200);
721 immut.setImmutable();
722 SkASSERT(!mut.isImmutable());
723 SkASSERT(immut.isImmutable());
724
725 // No one can hold a ref on our pixels yet.
726 REPORTER_ASSERT(r, mut.pixelRef()->unique());
727 REPORTER_ASSERT(r, immut.pixelRef()->unique());
728
reedca2622b2016-03-18 07:25:55 -0700729 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -0800730 {
731 // we want the recorder to go out of scope before our subsequent checks, so we
732 // place it inside local braces.
733 SkPictureRecorder rec;
734 SkCanvas* canvas = rec.beginRecording(1920, 1200);
735 canvas->drawBitmap(mut, 0, 0);
736 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -0700737 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -0800738 }
mtkleind72094d2014-08-27 12:12:23 -0700739
740 // The picture shares the immutable pixels but copies the mutable ones.
741 REPORTER_ASSERT(r, mut.pixelRef()->unique());
742 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
743
744 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -0700745 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -0700746 REPORTER_ASSERT(r, mut.pixelRef()->unique());
747 REPORTER_ASSERT(r, immut.pixelRef()->unique());
748}
mtkleinfeaadee2015-04-08 11:25:48 -0700749
750// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
751DEF_TEST(Picture_getRecordingCanvas, r) {
752 SkPictureRecorder rec;
753 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
754 for (int i = 0; i < 3; i++) {
755 rec.beginRecording(100, 100);
756 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -0700757 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -0700758 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
759 }
760}
mtklein9db912c2015-05-19 11:11:26 -0700761
Mike Kleina8ceb772019-05-20 12:45:50 +0000762DEF_TEST(MiniRecorderLeftHanging, r) {
763 // Any shader or other ref-counted effect will do just fine here.
764 SkPaint paint;
765 paint.setShader(SkShaders::Color(SK_ColorRED));
766
767 SkMiniRecorder rec;
768 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
769 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
770}
771
fmalita2ecc0002015-07-14 13:12:25 -0700772DEF_TEST(Picture_preserveCullRect, r) {
773 SkPictureRecorder recorder;
774
775 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
776 c->clear(SK_ColorCYAN);
777
reedca2622b2016-03-18 07:25:55 -0700778 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -0700779 SkDynamicMemoryWStream wstream;
780 picture->serialize(&wstream);
781
Ben Wagner145dbcd2016-11-03 14:40:50 -0400782 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
783 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -0700784
mtklein5f939ab2016-03-16 10:28:35 -0700785 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -0700786 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
787 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
788 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
789 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
790}
fmalita796e3652016-05-13 11:40:07 -0700791
Mike Klein26eb16f2017-04-10 09:50:25 -0400792
793// If we record bounded ops into a picture with a big cull and calculate the
794// bounds of those ops, we should trim down the picture cull to the ops' bounds.
795// If we're not using an SkBBH, we shouldn't change it.
796DEF_TEST(Picture_UpdatedCull_1, r) {
Mike Kleina8ceb772019-05-20 12:45:50 +0000797 // Testing 1 draw exercises SkMiniPicture.
Mike Klein26eb16f2017-04-10 09:50:25 -0400798 SkRTreeFactory factory;
799 SkPictureRecorder recorder;
800
Mike Reed274218e2018-01-08 15:05:02 -0500801 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400802 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
803 auto pic = recorder.finishRecordingAsPicture();
804 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
805
Mike Reed274218e2018-01-08 15:05:02 -0500806 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400807 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
808 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500809 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400810}
811DEF_TEST(Picture_UpdatedCull_2, r) {
812 // Testing >1 draw exercises SkBigPicture.
813 SkRTreeFactory factory;
814 SkPictureRecorder recorder;
815
Mike Reed274218e2018-01-08 15:05:02 -0500816 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400817 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
818 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
819 auto pic = recorder.finishRecordingAsPicture();
820 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
821
Mike Reed274218e2018-01-08 15:05:02 -0500822 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400823 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
824 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
825 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500826 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400827}
Mike Klein7cc49d62017-08-14 10:39:28 -0400828
Mike Klein88d90712018-01-27 17:30:04 +0000829DEF_TEST(Picture_RecordsFlush, r) {
830 SkPictureRecorder recorder;
831
832 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
833 for (int i = 0; i < 10; i++) {
834 canvas->clear(0);
835 for (int j = 0; j < 10; j++) {
836 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
837 }
838 canvas->flush();
839 }
840
841 // Did we record the flushes?
842 auto pic = recorder.finishRecordingAsPicture();
843 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
844
845 // Do we serialize and deserialize flushes?
846 auto skp = pic->serialize();
847 auto back = SkPicture::MakeFromData(skp->data(), skp->size());
848 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
849}
Mike Kleinfbe66202018-01-26 09:49:48 -0500850
851DEF_TEST(Placeholder, r) {
852 SkRect cull = { 0,0, 10,20 };
853
854 // Each placeholder is unique.
855 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
856 p2 = SkPicture::MakePlaceholder(cull);
857 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
858 REPORTER_ASSERT(r, p1->cullRect() == cull);
859 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
860
861 // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
862 SkPictureRecorder recorder;
863 SkCanvas* canvas = recorder.beginRecording(cull);
864 canvas->drawPicture(p1);
865 canvas->drawPicture(p2);
866 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
867 REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
868}
Mike Reed40d82972018-02-01 14:45:50 -0500869
870DEF_TEST(Picture_empty_serial, reporter) {
871 SkPictureRecorder rec;
872 (void)rec.beginRecording(10, 10);
873 auto pic = rec.finishRecordingAsPicture();
874 REPORTER_ASSERT(reporter, pic);
875
876 auto data = pic->serialize();
877 REPORTER_ASSERT(reporter, data);
878
879 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
880 REPORTER_ASSERT(reporter, pic2);
881}
882
Mike Klein6d20d992019-09-12 12:51:27 -0500883
884DEF_TEST(Picture_drawsNothing, r) {
885 // Tests that pic->cullRect().isEmpty() is a good way to test a picture
886 // recorded with an R-tree draws nothing.
887 struct {
888 bool draws_nothing;
889 void (*fn)(SkCanvas*);
890 } cases[] = {
891 { true, [](SkCanvas* c) { } },
892 { true, [](SkCanvas* c) { c->save(); c->restore(); } },
893 { true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5}); c->restore(); } },
894 { true, [](SkCanvas* c) { c->clipRect({0,0,5,5}); } },
895
896 { false, [](SkCanvas* c) { c->drawRect({0,0,5,5}, SkPaint{}); } },
897 { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
898 { false, [](SkCanvas* c) {
899 c->drawRect({0,0, 5, 5}, SkPaint{});
900 c->drawRect({5,5,10,10}, SkPaint{});
901 }},
902 };
903
904 for (const auto& c : cases) {
905 SkPictureRecorder rec;
906 SkRTreeFactory factory;
907 c.fn(rec.beginRecording(10,10, &factory));
908 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
909
910 REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
911 }
912}
Mike Kleinedf2d722019-10-02 10:53:39 -0500913
914DEF_TEST(Picture_emptyNestedPictureBug, r) {
915 const SkRect bounds = {-5000, -5000, 5000, 5000};
916
917 SkPictureRecorder recorder;
918 SkRTreeFactory factory;
919
920 // These three pictures should all draw the same but due to bugs they don't:
921 //
922 // 1) inner has enough content that it is recoreded as an SkBigPicture,
923 // and all its content falls outside the positive/positive quadrant,
924 // and it is recorded with an R-tree so we contract the cullRect to those bounds;
925 //
926 // 2) middle wraps inner,
927 // and it its recorded with an R-tree so we update middle's cullRect to inner's;
928 //
929 // 3) outer wraps inner,
930 // and notices that middle contains only one op, drawPicture(inner),
931 // so it plays middle back during recording rather than ref'ing middle,
932 // querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
933 // finding nothing to draw.
934 //
935 // * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
936 {
937 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
938 canvas->translate(-100,-100);
939 canvas->drawRect({0,0,50,50}, SkPaint{});
940 }
941 sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
942
943 recorder.beginRecording(bounds, &factory)->drawPicture(inner);
944 sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
945
946 // This doesn't need &factory to reproduce the bug,
947 // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
948 recorder.beginRecording(bounds, &factory)->drawPicture(middle);
949 sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
950
951 REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
952 REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
953 REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50})); // Used to fail.
954}
Mike Klein196e9fb2020-01-21 10:48:46 -0600955
956DEF_TEST(Picture_fillsBBH, r) {
957 // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
958 const SkRect rects[] = {
959 { 0, 0, 20,20},
960 {20,20, 40,40},
961 };
962
963 for (int n = 0; n <= 2; n++) {
964 SkRTreeFactory factory;
965 SkPictureRecorder rec;
966
967 sk_sp<SkBBoxHierarchy> bbh = factory();
968
969 SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
970 for (int i = 0; i < n; i++) {
971 c->drawRect(rects[i], SkPaint{});
972 }
973 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
974
Mike Klein1feceb32020-01-21 11:16:37 -0600975 auto base = (const SkBBoxHierarchy_Base*)bbh.get();
Mike Klein196e9fb2020-01-21 10:48:46 -0600976 SkTDArray<int> results;
Mike Klein1feceb32020-01-21 11:16:37 -0600977 base->search({0,0, 100,100}, &results);
Mike Klein196e9fb2020-01-21 10:48:46 -0600978 REPORTER_ASSERT(r, results.count() == n,
979 "results.count() == %d, want %d\n", results.count(), n);
980 }
981}