blob: d7ff8a4a8213ca7d49311b84c92cc61e9153f985 [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
Ben Wagnercb3d49c2018-03-14 15:07:43 -04008#include "SkBBHFactory.h"
mtklein3e8232b2014-08-18 13:39:11 -07009#include "SkBBoxHierarchy.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040010#include "SkBigPicture.h"
11#include "SkBitmap.h"
reed@google.com21b519d2012-10-02 17:42:15 +000012#include "SkCanvas.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040013#include "SkClipOp.h"
14#include "SkClipOpPriv.h"
15#include "SkColor.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000016#include "SkData.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040017#include "SkFontStyle.h"
18#include "SkImageInfo.h"
19#include "SkMatrix.h"
Mike Reed274218e2018-01-08 15:05:02 -050020#include "SkMiniRecorder.h"
reed@google.com21b519d2012-10-02 17:42:15 +000021#include "SkPaint.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040022#include "SkPath.h"
Cary Clarkefd99cc2018-06-11 16:25:43 -040023#include "SkPicturePriv.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000024#include "SkPictureRecorder.h"
mtkleind72094d2014-08-27 12:12:23 -070025#include "SkPixelRef.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000026#include "SkRandom.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040027#include "SkRect.h"
28#include "SkRectPriv.h"
29#include "SkRefCnt.h"
30#include "SkScalar.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000031#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000032#include "SkStream.h"
Ben Wagnercb3d49c2018-03-14 15:07:43 -040033#include "SkTypeface.h"
34#include "SkTypes.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000035#include "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)
111 , 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
mtklein36352bf2015-03-25 18:17:31 -0700119 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000120 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400121 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000122 }
123
mtklein36352bf2015-03-25 18:17:31 -0700124 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000125 ++fRestoreCount;
126 this->INHERITED::willRestore();
127 }
128
129 unsigned int getSaveCount() const { return fSaveCount; }
130 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
131 unsigned int getRestoreCount() const { return fRestoreCount; }
132
133private:
134 unsigned int fSaveCount;
135 unsigned int fSaveLayerCount;
136 unsigned int fRestoreCount;
137
138 typedef SkCanvas INHERITED;
139};
140
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000141void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000142 unsigned int numSaves, unsigned int numSaveLayers,
143 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700144 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700145 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000146
robertphillipsc5ba71d2014-09-04 08:42:50 -0700147 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000148
mtklein87c41382014-09-08 07:31:18 -0700149 // Optimizations may have removed these,
150 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
151 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
152 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
153 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000154}
155
156// This class exists so SkPicture can friend it and give it access to
157// the 'partialReplay' method.
158class SkPictureRecorderReplayTester {
159public:
reedca2622b2016-03-18 07:25:55 -0700160 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000161 SkPictureRecorder recorder2;
162
robertphillips9f1c2412014-06-09 06:25:34 -0700163 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000164
165 recorder->partialReplay(canvas);
166
reedca2622b2016-03-18 07:25:55 -0700167 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000168 }
169};
170
robertphillips9058d602014-06-10 11:45:46 -0700171static void create_imbalance(SkCanvas* canvas) {
172 SkRect clipRect = SkRect::MakeWH(2, 2);
173 SkRect drawRect = SkRect::MakeWH(10, 10);
174 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500175 canvas->clipRect(clipRect, kReplace_SkClipOp);
robertphillips9058d602014-06-10 11:45:46 -0700176 canvas->translate(1.0f, 1.0f);
177 SkPaint p;
178 p.setColor(SK_ColorGREEN);
179 canvas->drawRect(drawRect, p);
180 // no restore
181}
182
183// This tests that replaying a potentially unbalanced picture into a canvas
184// doesn't affect the canvas' save count or matrix/clip state.
185static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
186 SkBitmap bm;
187 bm.allocN32Pixels(4, 3);
188 SkCanvas canvas(bm);
189
190 int beforeSaveCount = canvas.getSaveCount();
191
192 SkMatrix beforeMatrix = canvas.getTotalMatrix();
193
Mike Reed918e1442017-01-23 11:39:45 -0500194 SkRect beforeClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700195
196 canvas.drawPicture(picture);
197
198 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
199 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
200
Mike Reed918e1442017-01-23 11:39:45 -0500201 SkRect afterClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700202
203 REPORTER_ASSERT(reporter, afterClip == beforeClip);
204}
205
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000206// Test out SkPictureRecorder::partialReplay
207DEF_TEST(PictureRecorder_replay, reporter) {
208 // check save/saveLayer state
209 {
210 SkPictureRecorder recorder;
211
robertphillips9f1c2412014-06-09 06:25:34 -0700212 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000213
halcanary96fcdcc2015-08-27 07:41:13 -0700214 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000215
reedca2622b2016-03-18 07:25:55 -0700216 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000217
218 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700219 check_save_state(reporter, copy.get(), 2, 1, 3);
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> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000224
reedca2622b2016-03-18 07:25:55 -0700225 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000226
227 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700228 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000229 }
230
robertphillips9058d602014-06-10 11:45:46 -0700231 // Recreate the Android partialReplay test case
232 {
233 SkPictureRecorder recorder;
234
halcanary96fcdcc2015-08-27 07:41:13 -0700235 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700236 create_imbalance(canvas);
237
238 int expectedSaveCount = canvas->getSaveCount();
239
reedca2622b2016-03-18 07:25:55 -0700240 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
241 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700242
243 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
244
245 // End the recording of source to test the picture finalization
246 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700247 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700248 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000249}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000250
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000251static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
252 SkCanvas testCanvas(100, 100);
253 set_canvas_to_save_count_4(&testCanvas);
254
255 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
256
257 SkPaint paint;
258 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
259
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000260 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000261
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000262 {
263 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700264 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000265 canvas->save();
266 canvas->translate(10, 10);
267 canvas->drawRect(rect, paint);
268 canvas->save();
269 canvas->translate(10, 10);
270 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700271 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000272
robertphillips9b14f262014-06-04 05:40:44 -0700273 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000274 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
275 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000276
277 set_canvas_to_save_count_4(&testCanvas);
278
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000279 {
280 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700281 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000282 canvas->save();
283 canvas->translate(10, 10);
284 canvas->drawRect(rect, paint);
285 canvas->save();
286 canvas->translate(10, 10);
287 canvas->drawRect(rect, paint);
288 canvas->restore();
289 canvas->restore();
290 canvas->restore();
291 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700292 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000293
robertphillips9b14f262014-06-04 05:40:44 -0700294 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000295 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
296 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000297
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000298 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000299
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000300 {
robertphillips9f1c2412014-06-09 06:25:34 -0700301 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000302 canvas->translate(10, 10);
303 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700304 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000305
robertphillips9b14f262014-06-04 05:40:44 -0700306 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000307 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
308 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
309 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000310}
311
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000312static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000313 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000314
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000315 SkPictureRecorder recorder;
316
reed@google.com21b519d2012-10-02 17:42:15 +0000317 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000318 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000319
robertphillips9f1c2412014-06-09 06:25:34 -0700320 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000321
322 for (int i = 0; i < 1000; ++i) {
323 rand_op(canvas, rand);
324 }
reedca2622b2016-03-18 07:25:55 -0700325 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000326
327 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000328 }
329
330 {
robertphillips9f1c2412014-06-09 06:25:34 -0700331 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000332 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000333
reed@google.com21b519d2012-10-02 17:42:15 +0000334 for (int i = 0; i < 100; ++i) {
335 canvas->save();
336 }
337 while (canvas->getSaveCount() > 1) {
338 canvas->clipRect(rect);
339 canvas->restore();
340 }
reedca2622b2016-03-18 07:25:55 -0700341 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000342 }
343}
344
scroggo@google.com4b90b112012-12-04 15:08:56 +0000345#ifndef SK_DEBUG
346// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
347// should never do this.
348static void test_bad_bitmap() {
349 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
350 // fail.
351 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000352 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000353 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700354 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000355 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700356 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000357
358 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700359 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000360}
361#endif
362
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000363static void test_clip_bound_opt(skiatest::Reporter* reporter) {
364 // Test for crbug.com/229011
365 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
366 SkIntToScalar(2), SkIntToScalar(2));
367 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
368 SkIntToScalar(1), SkIntToScalar(1));
369 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
370 SkIntToScalar(1), SkIntToScalar(1));
371
372 SkPath invPath;
373 invPath.addOval(rect1);
374 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
375 SkPath path;
376 path.addOval(rect2);
377 SkPath path2;
378 path2.addOval(rect3);
379 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000380 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700381
382 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000383 {
robertphillips9f1c2412014-06-09 06:25:34 -0700384 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700385 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500386 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000387 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
388 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
389 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
390 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
391 }
392 {
robertphillips9f1c2412014-06-09 06:25:34 -0700393 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700394 canvas->clipPath(path);
395 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500396 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000397 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
398 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
399 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
400 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
401 }
402 {
robertphillips9f1c2412014-06-09 06:25:34 -0700403 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700404 canvas->clipPath(path);
Mike Reedc1f77742016-12-09 09:00:50 -0500405 canvas->clipPath(invPath, kUnion_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500406 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000407 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
408 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
409 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
410 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
411 }
412 {
robertphillips9f1c2412014-06-09 06:25:34 -0700413 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500414 canvas->clipPath(path, kDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500415 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000416 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
417 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
418 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
419 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
420 }
421 {
robertphillips9f1c2412014-06-09 06:25:34 -0700422 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500423 canvas->clipPath(path, kReverseDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500424 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000425 // True clip is actually empty in this case, but the best
426 // determination we can make using only bounds as input is that the
427 // clip is included in the bounds of 'path'.
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000428 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
429 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
430 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
431 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
432 }
433 {
robertphillips9f1c2412014-06-09 06:25:34 -0700434 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500435 canvas->clipPath(path, kIntersect_SkClipOp);
436 canvas->clipPath(path2, kXOR_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500437 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000438 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
439 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
440 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
441 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
442 }
443}
444
schenneyeeff8bb2015-07-07 14:27:10 -0700445static void test_cull_rect_reset(skiatest::Reporter* reporter) {
446 SkPictureRecorder recorder;
447 SkRect bounds = SkRect::MakeWH(10, 10);
448 SkRTreeFactory factory;
449 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
450 bounds = SkRect::MakeWH(100, 100);
451 SkPaint paint;
452 canvas->drawRect(bounds, paint);
453 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700454 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
Cary Clarkefd99cc2018-06-11 16:25:43 -0400455 const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
schenneyeeff8bb2015-07-07 14:27:10 -0700456 REPORTER_ASSERT(reporter, picture);
457
458 SkRect finalCullRect = picture->cullRect();
459 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
460 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
461 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
462 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
463
464 const SkBBoxHierarchy* pictureBBH = picture->bbh();
465 SkRect bbhCullRect = pictureBBH->getRootBound();
466 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
467 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
468 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
469 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
470}
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);
553 SkPaint paint;
Ben Wagner71319502017-07-27 10:45:29 -0400554 paint.setTypeface(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
Cary Clark2a475ea2017-04-28 15:35:12 -0400555 canvas->drawString("Q", 0, 10, paint);
reedca2622b2016-03-18 07:25:55 -0700556 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700557 SkDynamicMemoryWStream stream;
558 picture->serialize(&stream);
559}
560
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000561DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700562 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000563#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700564 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000565 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000566#else
567 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000568#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000569 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000570 test_peephole();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000571 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000572 test_clip_expansion(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000573 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700574 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000575}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000576
577static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
578 const SkPaint paint;
579 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
580 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700581 int divs[] = { 2, 3 };
582 SkCanvas::Lattice lattice;
583 lattice.fXCount = lattice.fYCount = 2;
584 lattice.fXDivs = lattice.fYDivs = divs;
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000585
586 // Don't care what these record, as long as they're legal.
587 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -0700588 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000589 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -0800590 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -0700591 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000592}
593
594static void test_draw_bitmaps(SkCanvas* canvas) {
595 SkBitmap empty;
596 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000597 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000598 draw_bitmaps(empty, canvas);
599}
600
601DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000602 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700603 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700604 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000605}
606
607DEF_TEST(Canvas_EmptyBitmap, r) {
608 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000609 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000610 SkCanvas canvas(dst);
611
612 test_draw_bitmaps(&canvas);
613}
dneto3f22e8c2014-07-30 15:42:22 -0700614
615DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
616 // This test is from crbug.com/344987.
617 // The commands are:
618 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700619 // drawBitmapRect
620 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700621 // restore
622 // The bug was that this structure was modified so that:
623 // - The saveLayer and restore were eliminated
624 // - The alpha was only applied to the first drawBitmapRectToRect
625
626 // This test draws blue and red squares inside a 50% transparent
627 // layer. Both colours should show up muted.
628 // When the bug is present, the red square (the second bitmap)
629 // shows upwith full opacity.
630
631 SkBitmap blueBM;
632 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
633 SkBitmap redBM;
634 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
635 SkPaint semiTransparent;
636 semiTransparent.setAlpha(0x80);
637
638 SkPictureRecorder recorder;
639 SkCanvas* canvas = recorder.beginRecording(100, 100);
Mike Reed3661bc92017-02-22 13:21:42 -0500640 canvas->drawColor(0);
dneto3f22e8c2014-07-30 15:42:22 -0700641
Ben Wagnera93a14a2017-08-28 10:34:05 -0400642 canvas->saveLayer(nullptr, &semiTransparent);
dneto3f22e8c2014-07-30 15:42:22 -0700643 canvas->drawBitmap(blueBM, 25, 25);
644 canvas->drawBitmap(redBM, 50, 50);
645 canvas->restore();
646
reedca2622b2016-03-18 07:25:55 -0700647 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700648
649 // Now replay the picture back on another canvas
650 // and check a couple of its pixels.
651 SkBitmap replayBM;
652 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
653 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700654 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700655 replayCanvas.flush();
656
657 // With the bug present, at (55, 55) we would get a fully opaque red
658 // intead of a dark red.
659 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
660 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
661}
mtklein3e8232b2014-08-18 13:39:11 -0700662
663struct CountingBBH : public SkBBoxHierarchy {
664 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -0800665 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -0700666
schenney23d85932015-03-06 16:20:28 -0800667 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -0700668
mtkleinc6ad06a2015-08-19 09:51:00 -0700669 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700670 this->searchCalls++;
671 }
672
mtklein36352bf2015-03-25 18:17:31 -0700673 void insert(const SkRect[], int) override {}
674 virtual size_t bytesUsed() const override { return 0; }
675 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -0700676};
677
678class SpoonFedBBHFactory : public SkBBHFactory {
679public:
680 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -0700681 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700682 return SkRef(fBBH);
683 }
684private:
685 SkBBoxHierarchy* fBBH;
686};
687
688// When the canvas clip covers the full picture, we don't need to call the BBH.
689DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -0800690 SkRect bound = SkRect::MakeWH(320, 240);
691 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -0700692 SpoonFedBBHFactory factory(&bbh);
693
694 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -0700695 SkCanvas* c = recorder.beginRecording(bound, &factory);
696 // Record a few ops so we don't hit a small- or empty- picture optimization.
697 c->drawRect(bound, SkPaint());
698 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700699 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -0700700
701 SkCanvas big(640, 480), small(300, 200);
702
robertphillipsc5ba71d2014-09-04 08:42:50 -0700703 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -0700704 REPORTER_ASSERT(r, bbh.searchCalls == 0);
705
robertphillipsc5ba71d2014-09-04 08:42:50 -0700706 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -0700707 REPORTER_ASSERT(r, bbh.searchCalls == 1);
708}
mtkleind72094d2014-08-27 12:12:23 -0700709
710DEF_TEST(Picture_BitmapLeak, r) {
711 SkBitmap mut, immut;
712 mut.allocN32Pixels(300, 200);
713 immut.allocN32Pixels(300, 200);
714 immut.setImmutable();
715 SkASSERT(!mut.isImmutable());
716 SkASSERT(immut.isImmutable());
717
718 // No one can hold a ref on our pixels yet.
719 REPORTER_ASSERT(r, mut.pixelRef()->unique());
720 REPORTER_ASSERT(r, immut.pixelRef()->unique());
721
reedca2622b2016-03-18 07:25:55 -0700722 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -0800723 {
724 // we want the recorder to go out of scope before our subsequent checks, so we
725 // place it inside local braces.
726 SkPictureRecorder rec;
727 SkCanvas* canvas = rec.beginRecording(1920, 1200);
728 canvas->drawBitmap(mut, 0, 0);
729 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -0700730 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -0800731 }
mtkleind72094d2014-08-27 12:12:23 -0700732
733 // The picture shares the immutable pixels but copies the mutable ones.
734 REPORTER_ASSERT(r, mut.pixelRef()->unique());
735 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
736
737 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -0700738 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -0700739 REPORTER_ASSERT(r, mut.pixelRef()->unique());
740 REPORTER_ASSERT(r, immut.pixelRef()->unique());
741}
mtkleinfeaadee2015-04-08 11:25:48 -0700742
743// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
744DEF_TEST(Picture_getRecordingCanvas, r) {
745 SkPictureRecorder rec;
746 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
747 for (int i = 0; i < 3; i++) {
748 rec.beginRecording(100, 100);
749 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -0700750 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -0700751 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
752 }
753}
mtklein9db912c2015-05-19 11:11:26 -0700754
755DEF_TEST(MiniRecorderLeftHanging, r) {
756 // Any shader or other ref-counted effect will do just fine here.
757 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -0700758 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
mtklein9db912c2015-05-19 11:11:26 -0700759
760 SkMiniRecorder rec;
761 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
762 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
763}
fmalita2ecc0002015-07-14 13:12:25 -0700764
765DEF_TEST(Picture_preserveCullRect, r) {
766 SkPictureRecorder recorder;
767
768 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
769 c->clear(SK_ColorCYAN);
770
reedca2622b2016-03-18 07:25:55 -0700771 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -0700772 SkDynamicMemoryWStream wstream;
773 picture->serialize(&wstream);
774
Ben Wagner145dbcd2016-11-03 14:40:50 -0400775 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
776 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -0700777
mtklein5f939ab2016-03-16 10:28:35 -0700778 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -0700779 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
780 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
781 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
782 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
783}
fmalita796e3652016-05-13 11:40:07 -0700784
Mike Klein26eb16f2017-04-10 09:50:25 -0400785
786// If we record bounded ops into a picture with a big cull and calculate the
787// bounds of those ops, we should trim down the picture cull to the ops' bounds.
788// If we're not using an SkBBH, we shouldn't change it.
789DEF_TEST(Picture_UpdatedCull_1, r) {
790 // Testing 1 draw exercises SkMiniPicture.
791 SkRTreeFactory factory;
792 SkPictureRecorder recorder;
793
Mike Reed274218e2018-01-08 15:05:02 -0500794 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400795 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
796 auto pic = recorder.finishRecordingAsPicture();
797 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
798
Mike Reed274218e2018-01-08 15:05:02 -0500799 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400800 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
801 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500802 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400803}
804DEF_TEST(Picture_UpdatedCull_2, r) {
805 // Testing >1 draw exercises SkBigPicture.
806 SkRTreeFactory factory;
807 SkPictureRecorder recorder;
808
Mike Reed274218e2018-01-08 15:05:02 -0500809 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400810 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
811 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
812 auto pic = recorder.finishRecordingAsPicture();
813 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
814
Mike Reed274218e2018-01-08 15:05:02 -0500815 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400816 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
817 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
818 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500819 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400820}
Mike Klein7cc49d62017-08-14 10:39:28 -0400821
Mike Klein88d90712018-01-27 17:30:04 +0000822DEF_TEST(Picture_RecordsFlush, r) {
823 SkPictureRecorder recorder;
824
825 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
826 for (int i = 0; i < 10; i++) {
827 canvas->clear(0);
828 for (int j = 0; j < 10; j++) {
829 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
830 }
831 canvas->flush();
832 }
833
834 // Did we record the flushes?
835 auto pic = recorder.finishRecordingAsPicture();
836 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
837
838 // Do we serialize and deserialize flushes?
839 auto skp = pic->serialize();
840 auto back = SkPicture::MakeFromData(skp->data(), skp->size());
841 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
842}
Mike Kleinfbe66202018-01-26 09:49:48 -0500843
844DEF_TEST(Placeholder, r) {
845 SkRect cull = { 0,0, 10,20 };
846
847 // Each placeholder is unique.
848 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
849 p2 = SkPicture::MakePlaceholder(cull);
850 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
851 REPORTER_ASSERT(r, p1->cullRect() == cull);
852 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
853
854 // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
855 SkPictureRecorder recorder;
856 SkCanvas* canvas = recorder.beginRecording(cull);
857 canvas->drawPicture(p1);
858 canvas->drawPicture(p2);
859 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
860 REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
861}
Mike Reed40d82972018-02-01 14:45:50 -0500862
863DEF_TEST(Picture_empty_serial, reporter) {
864 SkPictureRecorder rec;
865 (void)rec.beginRecording(10, 10);
866 auto pic = rec.finishRecordingAsPicture();
867 REPORTER_ASSERT(reporter, pic);
868
869 auto data = pic->serialize();
870 REPORTER_ASSERT(reporter, data);
871
872 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
873 REPORTER_ASSERT(reporter, pic2);
874}
875