blob: c2c2cef867e092eabc81adb27be9f7b711fb7341 [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"
30#include "src/core/SkClipOpPriv.h"
Mike Kleina8ceb772019-05-20 12:45:50 +000031#include "src/core/SkMiniRecorder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/core/SkPicturePriv.h"
33#include "src/core/SkRectPriv.h"
34#include "tests/Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000035
Ben Wagnercb3d49c2018-03-14 15:07:43 -040036#include <memory>
37
38class SkRRect;
39class SkRegion;
40template <typename T> class SkTDArray;
41
reed@google.com47b679b2014-05-14 18:58:16 +000042
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000044 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000045 bm->eraseColor(color);
46 if (immutable) {
47 bm->setImmutable();
48 }
49}
50
scroggo@google.comd614c6a2012-09-14 17:26:37 +000051#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070052// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070053// in debug mode, so only run in debug mode.
54static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000055 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000056 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070057 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070058 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -070059 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -070060 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070061 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000062}
63
64// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
65static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000066 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070067 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -070068 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +000069 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +000070 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000071}
72#endif
73
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000074static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +000075 SkPaint paint;
76 SkRect rect = SkRect::MakeWH(50, 50);
77
78 SkScalar unit = rand.nextUScalar1();
79 if (unit <= 0.3) {
80// SkDebugf("save\n");
81 canvas->save();
82 } else if (unit <= 0.6) {
83// SkDebugf("restore\n");
84 canvas->restore();
85 } else if (unit <= 0.9) {
86// SkDebugf("clip\n");
87 canvas->clipRect(rect);
88 } else {
89// SkDebugf("draw\n");
90 canvas->drawPaint(paint);
91 }
92}
93
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +000094static void set_canvas_to_save_count_4(SkCanvas* canvas) {
95 canvas->restoreToCount(1);
96 canvas->save();
97 canvas->save();
98 canvas->save();
99}
100
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000101/**
102 * A canvas that records the number of saves, saveLayers and restores.
103 */
104class SaveCountingCanvas : public SkCanvas {
105public:
106 SaveCountingCanvas(int width, int height)
107 : INHERITED(width, height)
108 , fSaveCount(0)
109 , fSaveLayerCount(0)
Mike Reed148b7fd2018-12-18 17:38:18 -0500110 , fSaveBehindCount(0)
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000111 , fRestoreCount(0){
112 }
113
reed4960eee2015-12-18 07:09:18 -0800114 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000115 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800116 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000117 }
118
Mike Reed148b7fd2018-12-18 17:38:18 -0500119 bool onDoSaveBehind(const SkRect* subset) override {
120 ++fSaveBehindCount;
121 return this->INHERITED::onDoSaveBehind(subset);
122 }
123
mtklein36352bf2015-03-25 18:17:31 -0700124 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000125 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400126 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000127 }
128
mtklein36352bf2015-03-25 18:17:31 -0700129 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000130 ++fRestoreCount;
131 this->INHERITED::willRestore();
132 }
133
134 unsigned int getSaveCount() const { return fSaveCount; }
135 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
Mike Reed148b7fd2018-12-18 17:38:18 -0500136 unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000137 unsigned int getRestoreCount() const { return fRestoreCount; }
138
139private:
140 unsigned int fSaveCount;
141 unsigned int fSaveLayerCount;
Mike Reed148b7fd2018-12-18 17:38:18 -0500142 unsigned int fSaveBehindCount;
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000143 unsigned int fRestoreCount;
144
145 typedef SkCanvas INHERITED;
146};
147
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000148void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000149 unsigned int numSaves, unsigned int numSaveLayers,
150 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700151 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700152 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000153
robertphillipsc5ba71d2014-09-04 08:42:50 -0700154 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000155
mtklein87c41382014-09-08 07:31:18 -0700156 // Optimizations may have removed these,
157 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
158 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
159 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
160 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000161}
162
163// This class exists so SkPicture can friend it and give it access to
164// the 'partialReplay' method.
165class SkPictureRecorderReplayTester {
166public:
reedca2622b2016-03-18 07:25:55 -0700167 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000168 SkPictureRecorder recorder2;
169
robertphillips9f1c2412014-06-09 06:25:34 -0700170 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000171
172 recorder->partialReplay(canvas);
173
reedca2622b2016-03-18 07:25:55 -0700174 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000175 }
176};
177
robertphillips9058d602014-06-10 11:45:46 -0700178static void create_imbalance(SkCanvas* canvas) {
179 SkRect clipRect = SkRect::MakeWH(2, 2);
180 SkRect drawRect = SkRect::MakeWH(10, 10);
181 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500182 canvas->clipRect(clipRect, kReplace_SkClipOp);
robertphillips9058d602014-06-10 11:45:46 -0700183 canvas->translate(1.0f, 1.0f);
184 SkPaint p;
185 p.setColor(SK_ColorGREEN);
186 canvas->drawRect(drawRect, p);
187 // no restore
188}
189
190// This tests that replaying a potentially unbalanced picture into a canvas
191// doesn't affect the canvas' save count or matrix/clip state.
192static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
193 SkBitmap bm;
194 bm.allocN32Pixels(4, 3);
195 SkCanvas canvas(bm);
196
197 int beforeSaveCount = canvas.getSaveCount();
198
199 SkMatrix beforeMatrix = canvas.getTotalMatrix();
200
Mike Reed918e1442017-01-23 11:39:45 -0500201 SkRect beforeClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700202
203 canvas.drawPicture(picture);
204
205 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
206 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
207
Mike Reed918e1442017-01-23 11:39:45 -0500208 SkRect afterClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700209
210 REPORTER_ASSERT(reporter, afterClip == beforeClip);
211}
212
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000213// Test out SkPictureRecorder::partialReplay
214DEF_TEST(PictureRecorder_replay, reporter) {
215 // check save/saveLayer state
216 {
217 SkPictureRecorder recorder;
218
robertphillips9f1c2412014-06-09 06:25:34 -0700219 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000220
halcanary96fcdcc2015-08-27 07:41:13 -0700221 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000222
reedca2622b2016-03-18 07:25:55 -0700223 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000224
225 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700226 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000227
halcanary96fcdcc2015-08-27 07:41:13 -0700228 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000229
reedca2622b2016-03-18 07:25:55 -0700230 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000231
reedca2622b2016-03-18 07:25:55 -0700232 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000233
234 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700235 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000236 }
237
robertphillips9058d602014-06-10 11:45:46 -0700238 // Recreate the Android partialReplay test case
239 {
240 SkPictureRecorder recorder;
241
halcanary96fcdcc2015-08-27 07:41:13 -0700242 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700243 create_imbalance(canvas);
244
245 int expectedSaveCount = canvas->getSaveCount();
246
reedca2622b2016-03-18 07:25:55 -0700247 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
248 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700249
250 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
251
252 // End the recording of source to test the picture finalization
253 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700254 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700255 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000256}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000257
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000258static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
259 SkCanvas testCanvas(100, 100);
260 set_canvas_to_save_count_4(&testCanvas);
261
262 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
263
264 SkPaint paint;
265 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
266
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000267 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000268
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000269 {
270 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700271 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000272 canvas->save();
273 canvas->translate(10, 10);
274 canvas->drawRect(rect, paint);
275 canvas->save();
276 canvas->translate(10, 10);
277 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700278 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000279
robertphillips9b14f262014-06-04 05:40:44 -0700280 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000281 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
282 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000283
284 set_canvas_to_save_count_4(&testCanvas);
285
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000286 {
287 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700288 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000289 canvas->save();
290 canvas->translate(10, 10);
291 canvas->drawRect(rect, paint);
292 canvas->save();
293 canvas->translate(10, 10);
294 canvas->drawRect(rect, paint);
295 canvas->restore();
296 canvas->restore();
297 canvas->restore();
298 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700299 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000300
robertphillips9b14f262014-06-04 05:40:44 -0700301 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000302 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
303 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000304
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000305 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000306
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000307 {
robertphillips9f1c2412014-06-09 06:25:34 -0700308 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000309 canvas->translate(10, 10);
310 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700311 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000312
robertphillips9b14f262014-06-04 05:40:44 -0700313 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000314 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
315 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
316 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000317}
318
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000319static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000320 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000321
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000322 SkPictureRecorder recorder;
323
reed@google.com21b519d2012-10-02 17:42:15 +0000324 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000325 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000326
robertphillips9f1c2412014-06-09 06:25:34 -0700327 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000328
329 for (int i = 0; i < 1000; ++i) {
330 rand_op(canvas, rand);
331 }
reedca2622b2016-03-18 07:25:55 -0700332 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000333
334 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000335 }
336
337 {
robertphillips9f1c2412014-06-09 06:25:34 -0700338 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000339 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000340
reed@google.com21b519d2012-10-02 17:42:15 +0000341 for (int i = 0; i < 100; ++i) {
342 canvas->save();
343 }
344 while (canvas->getSaveCount() > 1) {
345 canvas->clipRect(rect);
346 canvas->restore();
347 }
reedca2622b2016-03-18 07:25:55 -0700348 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000349 }
350}
351
scroggo@google.com4b90b112012-12-04 15:08:56 +0000352#ifndef SK_DEBUG
353// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
354// should never do this.
355static void test_bad_bitmap() {
356 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
357 // fail.
358 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000359 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000360 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700361 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000362 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700363 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000364
365 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700366 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000367}
368#endif
369
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000370static void test_clip_bound_opt(skiatest::Reporter* reporter) {
371 // Test for crbug.com/229011
372 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
373 SkIntToScalar(2), SkIntToScalar(2));
374 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
375 SkIntToScalar(1), SkIntToScalar(1));
376 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
377 SkIntToScalar(1), SkIntToScalar(1));
378
379 SkPath invPath;
380 invPath.addOval(rect1);
Mike Reed7d34dc72019-11-26 12:17:17 -0500381 invPath.setFillType(SkPathFillType::kInverseEvenOdd);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000382 SkPath path;
383 path.addOval(rect2);
384 SkPath path2;
385 path2.addOval(rect3);
386 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000387 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700388
389 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000390 {
robertphillips9f1c2412014-06-09 06:25:34 -0700391 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700392 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500393 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000394 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
395 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
396 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
397 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
398 }
399 {
robertphillips9f1c2412014-06-09 06:25:34 -0700400 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700401 canvas->clipPath(path);
402 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500403 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000404 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
405 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
406 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
407 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
408 }
409 {
robertphillips9f1c2412014-06-09 06:25:34 -0700410 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700411 canvas->clipPath(path);
Mike Reedc1f77742016-12-09 09:00:50 -0500412 canvas->clipPath(invPath, kUnion_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500413 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000414 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
415 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
416 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
417 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
418 }
419 {
robertphillips9f1c2412014-06-09 06:25:34 -0700420 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500421 canvas->clipPath(path, kDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500422 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000423 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
424 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
425 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
426 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
427 }
428 {
robertphillips9f1c2412014-06-09 06:25:34 -0700429 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500430 canvas->clipPath(path, kReverseDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500431 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000432 // True clip is actually empty in this case, but the best
433 // determination we can make using only bounds as input is that the
434 // clip is included in the bounds of 'path'.
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000435 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
436 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
437 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
438 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
439 }
440 {
robertphillips9f1c2412014-06-09 06:25:34 -0700441 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500442 canvas->clipPath(path, kIntersect_SkClipOp);
443 canvas->clipPath(path2, kXOR_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500444 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000445 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
446 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
447 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
448 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
449 }
450}
451
schenneyeeff8bb2015-07-07 14:27:10 -0700452static void test_cull_rect_reset(skiatest::Reporter* reporter) {
453 SkPictureRecorder recorder;
454 SkRect bounds = SkRect::MakeWH(10, 10);
455 SkRTreeFactory factory;
456 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
457 bounds = SkRect::MakeWH(100, 100);
458 SkPaint paint;
459 canvas->drawRect(bounds, paint);
460 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700461 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
Cary Clarkefd99cc2018-06-11 16:25:43 -0400462 const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
schenneyeeff8bb2015-07-07 14:27:10 -0700463 REPORTER_ASSERT(reporter, picture);
464
465 SkRect finalCullRect = picture->cullRect();
466 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
467 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
468 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
469 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
schenneyeeff8bb2015-07-07 14:27:10 -0700470}
471
472
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000473/**
474 * A canvas that records the number of clip commands.
475 */
476class ClipCountingCanvas : public SkCanvas {
477public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000478 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000479 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000480 , fClipCount(0){
481 }
482
Mike Reedc1f77742016-12-09 09:00:50 -0500483 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000484 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000485 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000486 }
487
Mike Reedc1f77742016-12-09 09:00:50 -0500488 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000489 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000490 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000491 }
492
Mike Reedc1f77742016-12-09 09:00:50 -0500493 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000494 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000495 this->INHERITED::onClipPath(path, op, edgeStyle);
496 }
497
Mike Reedc1f77742016-12-09 09:00:50 -0500498 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000499 fClipCount += 1;
500 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000501 }
502
503 unsigned getClipCount() const { return fClipCount; }
504
505private:
506 unsigned fClipCount;
507
508 typedef SkCanvas INHERITED;
509};
510
511static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000512 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700513 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000514
Mike Reedc1f77742016-12-09 09:00:50 -0500515 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000516 // The following expanding clip should not be skipped.
Mike Reedc1f77742016-12-09 09:00:50 -0500517 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000518 // Draw something so the optimizer doesn't just fold the world.
519 SkPaint p;
520 p.setColor(SK_ColorBLUE);
521 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700522 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000523
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000524 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700525 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000526
527 // Both clips should be present on playback.
528 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
529}
530
robertphillips@google.comd5500882014-04-02 23:51:13 +0000531static void test_gen_id(skiatest::Reporter* reporter) {
532
Robert Phillipscfaeec42014-07-13 12:00:50 -0400533 SkPictureRecorder recorder;
534 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700535 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000536
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000537 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400538 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000539
robertphillips9f1c2412014-06-09 06:25:34 -0700540 SkCanvas* canvas = recorder.beginRecording(1, 1);
Mike Reed3661bc92017-02-22 13:21:42 -0500541 canvas->drawColor(SK_ColorWHITE);
reedca2622b2016-03-18 07:25:55 -0700542 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000543 // picture should have a non-zero id after recording
544 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000545
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000546 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400547 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000548}
549
caryclark5ef194c2015-08-31 09:22:38 -0700550static void test_typeface(skiatest::Reporter* reporter) {
551 SkPictureRecorder recorder;
552 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc4745d62019-01-07 09:31:58 -0500553 SkFont font(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
554 canvas->drawString("Q", 0, 10, font, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700555 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700556 SkDynamicMemoryWStream stream;
557 picture->serialize(&stream);
558}
559
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000560DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700561 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000562#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700563 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000564 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000565#else
566 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000567#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000568 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000569 test_peephole();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000570 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000571 test_clip_expansion(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000572 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700573 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000574}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000575
576static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
577 const SkPaint paint;
578 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000579
580 // Don't care what these record, as long as they're legal.
581 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -0700582 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
reedda420b92015-12-16 08:38:15 -0800583 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000584}
585
586static void test_draw_bitmaps(SkCanvas* canvas) {
587 SkBitmap empty;
588 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000589 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000590 draw_bitmaps(empty, canvas);
591}
592
593DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000594 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700595 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700596 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000597}
598
599DEF_TEST(Canvas_EmptyBitmap, r) {
600 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000601 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000602 SkCanvas canvas(dst);
603
604 test_draw_bitmaps(&canvas);
605}
dneto3f22e8c2014-07-30 15:42:22 -0700606
607DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
608 // This test is from crbug.com/344987.
609 // The commands are:
610 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700611 // drawBitmapRect
612 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700613 // restore
614 // The bug was that this structure was modified so that:
615 // - The saveLayer and restore were eliminated
616 // - The alpha was only applied to the first drawBitmapRectToRect
617
618 // This test draws blue and red squares inside a 50% transparent
619 // layer. Both colours should show up muted.
620 // When the bug is present, the red square (the second bitmap)
621 // shows upwith full opacity.
622
623 SkBitmap blueBM;
624 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
625 SkBitmap redBM;
626 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
627 SkPaint semiTransparent;
628 semiTransparent.setAlpha(0x80);
629
630 SkPictureRecorder recorder;
631 SkCanvas* canvas = recorder.beginRecording(100, 100);
Mike Reed3661bc92017-02-22 13:21:42 -0500632 canvas->drawColor(0);
dneto3f22e8c2014-07-30 15:42:22 -0700633
Ben Wagnera93a14a2017-08-28 10:34:05 -0400634 canvas->saveLayer(nullptr, &semiTransparent);
dneto3f22e8c2014-07-30 15:42:22 -0700635 canvas->drawBitmap(blueBM, 25, 25);
636 canvas->drawBitmap(redBM, 50, 50);
637 canvas->restore();
638
reedca2622b2016-03-18 07:25:55 -0700639 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700640
641 // Now replay the picture back on another canvas
642 // and check a couple of its pixels.
643 SkBitmap replayBM;
644 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
645 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700646 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700647
648 // With the bug present, at (55, 55) we would get a fully opaque red
649 // intead of a dark red.
650 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
651 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
652}
mtklein3e8232b2014-08-18 13:39:11 -0700653
Mike Klein508fd322020-02-12 09:45:41 -0600654struct CountingBBH : public SkBBoxHierarchy {
mtklein3e8232b2014-08-18 13:39:11 -0700655 mutable int searchCalls;
656
Mike Kleine1bad062020-02-11 16:16:12 -0600657 CountingBBH() : searchCalls(0) {}
mtklein3e8232b2014-08-18 13:39:11 -0700658
Mike Klein508fd322020-02-12 09:45:41 -0600659 void search(const SkRect& query, std::vector<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700660 this->searchCalls++;
661 }
662
mtklein36352bf2015-03-25 18:17:31 -0700663 void insert(const SkRect[], int) override {}
664 virtual size_t bytesUsed() const override { return 0; }
mtklein3e8232b2014-08-18 13:39:11 -0700665};
666
667class SpoonFedBBHFactory : public SkBBHFactory {
668public:
Mike Klein7a7ee942020-01-21 10:34:55 -0600669 explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(bbh) {}
670 sk_sp<SkBBoxHierarchy> operator()() const override {
671 return fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700672 }
673private:
Mike Klein7a7ee942020-01-21 10:34:55 -0600674 sk_sp<SkBBoxHierarchy> fBBH;
mtklein3e8232b2014-08-18 13:39:11 -0700675};
676
677// When the canvas clip covers the full picture, we don't need to call the BBH.
678DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -0800679 SkRect bound = SkRect::MakeWH(320, 240);
Mike Klein7a7ee942020-01-21 10:34:55 -0600680
Mike Kleine1bad062020-02-11 16:16:12 -0600681 auto bbh = sk_make_sp<CountingBBH>();
Mike Klein7a7ee942020-01-21 10:34:55 -0600682 SpoonFedBBHFactory factory(bbh);
mtklein3e8232b2014-08-18 13:39:11 -0700683
684 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -0700685 SkCanvas* c = recorder.beginRecording(bound, &factory);
686 // Record a few ops so we don't hit a small- or empty- picture optimization.
687 c->drawRect(bound, SkPaint());
688 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700689 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -0700690
691 SkCanvas big(640, 480), small(300, 200);
692
robertphillipsc5ba71d2014-09-04 08:42:50 -0700693 picture->playback(&big);
Mike Klein7a7ee942020-01-21 10:34:55 -0600694 REPORTER_ASSERT(r, bbh->searchCalls == 0);
mtklein3e8232b2014-08-18 13:39:11 -0700695
robertphillipsc5ba71d2014-09-04 08:42:50 -0700696 picture->playback(&small);
Mike Klein7a7ee942020-01-21 10:34:55 -0600697 REPORTER_ASSERT(r, bbh->searchCalls == 1);
mtklein3e8232b2014-08-18 13:39:11 -0700698}
mtkleind72094d2014-08-27 12:12:23 -0700699
700DEF_TEST(Picture_BitmapLeak, r) {
701 SkBitmap mut, immut;
702 mut.allocN32Pixels(300, 200);
703 immut.allocN32Pixels(300, 200);
704 immut.setImmutable();
705 SkASSERT(!mut.isImmutable());
706 SkASSERT(immut.isImmutable());
707
708 // No one can hold a ref on our pixels yet.
709 REPORTER_ASSERT(r, mut.pixelRef()->unique());
710 REPORTER_ASSERT(r, immut.pixelRef()->unique());
711
reedca2622b2016-03-18 07:25:55 -0700712 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -0800713 {
714 // we want the recorder to go out of scope before our subsequent checks, so we
715 // place it inside local braces.
716 SkPictureRecorder rec;
717 SkCanvas* canvas = rec.beginRecording(1920, 1200);
718 canvas->drawBitmap(mut, 0, 0);
719 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -0700720 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -0800721 }
mtkleind72094d2014-08-27 12:12:23 -0700722
723 // The picture shares the immutable pixels but copies the mutable ones.
724 REPORTER_ASSERT(r, mut.pixelRef()->unique());
725 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
726
727 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -0700728 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -0700729 REPORTER_ASSERT(r, mut.pixelRef()->unique());
730 REPORTER_ASSERT(r, immut.pixelRef()->unique());
731}
mtkleinfeaadee2015-04-08 11:25:48 -0700732
733// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
734DEF_TEST(Picture_getRecordingCanvas, r) {
735 SkPictureRecorder rec;
736 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
737 for (int i = 0; i < 3; i++) {
738 rec.beginRecording(100, 100);
739 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -0700740 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -0700741 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
742 }
743}
mtklein9db912c2015-05-19 11:11:26 -0700744
Mike Kleina8ceb772019-05-20 12:45:50 +0000745DEF_TEST(MiniRecorderLeftHanging, r) {
746 // Any shader or other ref-counted effect will do just fine here.
747 SkPaint paint;
748 paint.setShader(SkShaders::Color(SK_ColorRED));
749
750 SkMiniRecorder rec;
751 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
752 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
753}
754
fmalita2ecc0002015-07-14 13:12:25 -0700755DEF_TEST(Picture_preserveCullRect, r) {
756 SkPictureRecorder recorder;
757
758 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
759 c->clear(SK_ColorCYAN);
760
reedca2622b2016-03-18 07:25:55 -0700761 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -0700762 SkDynamicMemoryWStream wstream;
763 picture->serialize(&wstream);
764
Ben Wagner145dbcd2016-11-03 14:40:50 -0400765 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
766 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -0700767
mtklein5f939ab2016-03-16 10:28:35 -0700768 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -0700769 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
770 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
771 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
772 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
773}
fmalita796e3652016-05-13 11:40:07 -0700774
Mike Klein26eb16f2017-04-10 09:50:25 -0400775
776// If we record bounded ops into a picture with a big cull and calculate the
777// bounds of those ops, we should trim down the picture cull to the ops' bounds.
778// If we're not using an SkBBH, we shouldn't change it.
779DEF_TEST(Picture_UpdatedCull_1, r) {
Mike Kleina8ceb772019-05-20 12:45:50 +0000780 // Testing 1 draw exercises SkMiniPicture.
Mike Klein26eb16f2017-04-10 09:50:25 -0400781 SkRTreeFactory factory;
782 SkPictureRecorder recorder;
783
Mike Reed274218e2018-01-08 15:05:02 -0500784 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400785 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
786 auto pic = recorder.finishRecordingAsPicture();
787 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
788
Mike Reed274218e2018-01-08 15:05:02 -0500789 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400790 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
791 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500792 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400793}
794DEF_TEST(Picture_UpdatedCull_2, r) {
795 // Testing >1 draw exercises SkBigPicture.
796 SkRTreeFactory factory;
797 SkPictureRecorder recorder;
798
Mike Reed274218e2018-01-08 15:05:02 -0500799 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400800 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
801 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
802 auto pic = recorder.finishRecordingAsPicture();
803 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
804
Mike Reed274218e2018-01-08 15:05:02 -0500805 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400806 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
807 canvas->drawRect(SkRect::MakeWH(10,40), 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}
Mike Klein7cc49d62017-08-14 10:39:28 -0400811
Mike Klein88d90712018-01-27 17:30:04 +0000812DEF_TEST(Picture_RecordsFlush, r) {
813 SkPictureRecorder recorder;
814
815 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
816 for (int i = 0; i < 10; i++) {
817 canvas->clear(0);
818 for (int j = 0; j < 10; j++) {
819 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
820 }
821 canvas->flush();
822 }
823
824 // Did we record the flushes?
825 auto pic = recorder.finishRecordingAsPicture();
826 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
827
828 // Do we serialize and deserialize flushes?
829 auto skp = pic->serialize();
830 auto back = SkPicture::MakeFromData(skp->data(), skp->size());
831 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
832}
Mike Kleinfbe66202018-01-26 09:49:48 -0500833
834DEF_TEST(Placeholder, r) {
835 SkRect cull = { 0,0, 10,20 };
836
837 // Each placeholder is unique.
838 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
839 p2 = SkPicture::MakePlaceholder(cull);
840 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
841 REPORTER_ASSERT(r, p1->cullRect() == cull);
842 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
843
844 // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
845 SkPictureRecorder recorder;
846 SkCanvas* canvas = recorder.beginRecording(cull);
847 canvas->drawPicture(p1);
848 canvas->drawPicture(p2);
849 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
850 REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
851}
Mike Reed40d82972018-02-01 14:45:50 -0500852
853DEF_TEST(Picture_empty_serial, reporter) {
854 SkPictureRecorder rec;
855 (void)rec.beginRecording(10, 10);
856 auto pic = rec.finishRecordingAsPicture();
857 REPORTER_ASSERT(reporter, pic);
858
859 auto data = pic->serialize();
860 REPORTER_ASSERT(reporter, data);
861
862 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
863 REPORTER_ASSERT(reporter, pic2);
864}
865
Mike Klein6d20d992019-09-12 12:51:27 -0500866
867DEF_TEST(Picture_drawsNothing, r) {
868 // Tests that pic->cullRect().isEmpty() is a good way to test a picture
869 // recorded with an R-tree draws nothing.
870 struct {
871 bool draws_nothing;
872 void (*fn)(SkCanvas*);
873 } cases[] = {
874 { true, [](SkCanvas* c) { } },
875 { true, [](SkCanvas* c) { c->save(); c->restore(); } },
876 { true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5}); c->restore(); } },
877 { true, [](SkCanvas* c) { c->clipRect({0,0,5,5}); } },
878
879 { false, [](SkCanvas* c) { c->drawRect({0,0,5,5}, SkPaint{}); } },
880 { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
881 { false, [](SkCanvas* c) {
882 c->drawRect({0,0, 5, 5}, SkPaint{});
883 c->drawRect({5,5,10,10}, SkPaint{});
884 }},
885 };
886
887 for (const auto& c : cases) {
888 SkPictureRecorder rec;
889 SkRTreeFactory factory;
890 c.fn(rec.beginRecording(10,10, &factory));
891 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
892
893 REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
894 }
895}
Mike Kleinedf2d722019-10-02 10:53:39 -0500896
897DEF_TEST(Picture_emptyNestedPictureBug, r) {
898 const SkRect bounds = {-5000, -5000, 5000, 5000};
899
900 SkPictureRecorder recorder;
901 SkRTreeFactory factory;
902
903 // These three pictures should all draw the same but due to bugs they don't:
904 //
905 // 1) inner has enough content that it is recoreded as an SkBigPicture,
906 // and all its content falls outside the positive/positive quadrant,
907 // and it is recorded with an R-tree so we contract the cullRect to those bounds;
908 //
909 // 2) middle wraps inner,
910 // and it its recorded with an R-tree so we update middle's cullRect to inner's;
911 //
912 // 3) outer wraps inner,
913 // and notices that middle contains only one op, drawPicture(inner),
914 // so it plays middle back during recording rather than ref'ing middle,
915 // querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
916 // finding nothing to draw.
917 //
918 // * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
919 {
920 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
921 canvas->translate(-100,-100);
922 canvas->drawRect({0,0,50,50}, SkPaint{});
923 }
924 sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
925
926 recorder.beginRecording(bounds, &factory)->drawPicture(inner);
927 sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
928
929 // This doesn't need &factory to reproduce the bug,
930 // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
931 recorder.beginRecording(bounds, &factory)->drawPicture(middle);
932 sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
933
934 REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
935 REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
936 REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50})); // Used to fail.
937}
Mike Klein196e9fb2020-01-21 10:48:46 -0600938
939DEF_TEST(Picture_fillsBBH, r) {
940 // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
941 const SkRect rects[] = {
942 { 0, 0, 20,20},
943 {20,20, 40,40},
944 };
945
946 for (int n = 0; n <= 2; n++) {
947 SkRTreeFactory factory;
948 SkPictureRecorder rec;
949
950 sk_sp<SkBBoxHierarchy> bbh = factory();
951
952 SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
953 for (int i = 0; i < n; i++) {
954 c->drawRect(rects[i], SkPaint{});
955 }
956 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
957
Mike Klein508fd322020-02-12 09:45:41 -0600958 std::vector<int> results;
959 bbh->search({0,0, 100,100}, &results);
960 REPORTER_ASSERT(r, (int)results.size() == n,
961 "results.size() == %d, want %d\n", (int)results.size(), n);
Mike Klein196e9fb2020-01-21 10:48:46 -0600962 }
963}