blob: 425b25a153369b555047459584e388b8829ca918 [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
robertphillipsdda54452016-07-13 13:27:16 -07008#include "SkBigPicture.h"
mtklein3e8232b2014-08-18 13:39:11 -07009#include "SkBBoxHierarchy.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000010#include "SkBlurImageFilter.h"
reed@google.com21b519d2012-10-02 17:42:15 +000011#include "SkCanvas.h"
robertphillipsd8aa7b72014-10-30 16:45:02 -070012#include "SkColorMatrixFilter.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000013#include "SkColorPriv.h"
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +000014#include "SkDashPathEffect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000015#include "SkData.h"
reed5965c8a2015-01-07 18:04:45 -080016#include "SkImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000017#include "SkImageEncoder.h"
18#include "SkImageGenerator.h"
msarett8715d472016-02-17 10:02:29 -080019#include "SkMD5.h"
Mike Reed274218e2018-01-08 15:05:02 -050020#include "SkMiniRecorder.h"
reed@google.com21b519d2012-10-02 17:42:15 +000021#include "SkPaint.h"
Mike Klein88d90712018-01-27 17:30:04 +000022#include "SkPicture.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000023#include "SkPictureRecorder.h"
mtkleind72094d2014-08-27 12:12:23 -070024#include "SkPixelRef.h"
Mike Reed274218e2018-01-08 15:05:02 -050025#include "SkRectPriv.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000026#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000027#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080028#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000029#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000030#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070031#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000032
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000033#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000034
reed@google.com47b679b2014-05-14 18:58:16 +000035#include "SkLumaColorFilter.h"
36#include "SkColorFilterImageFilter.h"
37
reed@google.comfe7b1ed2012-11-29 21:00:39 +000038static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000039 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000040 bm->eraseColor(color);
41 if (immutable) {
42 bm->setImmutable();
43 }
44}
45
scroggo@google.comd614c6a2012-09-14 17:26:37 +000046#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070047// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070048// in debug mode, so only run in debug mode.
49static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000050 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000051 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070052 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070053 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -070054 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -070055 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070056 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000057}
58
59// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
60static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000061 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070062 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -070063 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +000064 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +000065 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000066}
67#endif
68
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000069static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +000070 SkPaint paint;
71 SkRect rect = SkRect::MakeWH(50, 50);
72
73 SkScalar unit = rand.nextUScalar1();
74 if (unit <= 0.3) {
75// SkDebugf("save\n");
76 canvas->save();
77 } else if (unit <= 0.6) {
78// SkDebugf("restore\n");
79 canvas->restore();
80 } else if (unit <= 0.9) {
81// SkDebugf("clip\n");
82 canvas->clipRect(rect);
83 } else {
84// SkDebugf("draw\n");
85 canvas->drawPaint(paint);
86 }
87}
88
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +000089static void set_canvas_to_save_count_4(SkCanvas* canvas) {
90 canvas->restoreToCount(1);
91 canvas->save();
92 canvas->save();
93 canvas->save();
94}
95
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +000096/**
97 * A canvas that records the number of saves, saveLayers and restores.
98 */
99class SaveCountingCanvas : public SkCanvas {
100public:
101 SaveCountingCanvas(int width, int height)
102 : INHERITED(width, height)
103 , fSaveCount(0)
104 , fSaveLayerCount(0)
105 , fRestoreCount(0){
106 }
107
reed4960eee2015-12-18 07:09:18 -0800108 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000109 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800110 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000111 }
112
mtklein36352bf2015-03-25 18:17:31 -0700113 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000114 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400115 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000116 }
117
mtklein36352bf2015-03-25 18:17:31 -0700118 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000119 ++fRestoreCount;
120 this->INHERITED::willRestore();
121 }
122
123 unsigned int getSaveCount() const { return fSaveCount; }
124 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
125 unsigned int getRestoreCount() const { return fRestoreCount; }
126
127private:
128 unsigned int fSaveCount;
129 unsigned int fSaveLayerCount;
130 unsigned int fRestoreCount;
131
132 typedef SkCanvas INHERITED;
133};
134
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000135void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000136 unsigned int numSaves, unsigned int numSaveLayers,
137 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700138 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700139 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000140
robertphillipsc5ba71d2014-09-04 08:42:50 -0700141 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000142
mtklein87c41382014-09-08 07:31:18 -0700143 // Optimizations may have removed these,
144 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
145 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
146 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
147 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000148}
149
150// This class exists so SkPicture can friend it and give it access to
151// the 'partialReplay' method.
152class SkPictureRecorderReplayTester {
153public:
reedca2622b2016-03-18 07:25:55 -0700154 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000155 SkPictureRecorder recorder2;
156
robertphillips9f1c2412014-06-09 06:25:34 -0700157 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000158
159 recorder->partialReplay(canvas);
160
reedca2622b2016-03-18 07:25:55 -0700161 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000162 }
163};
164
robertphillips9058d602014-06-10 11:45:46 -0700165static void create_imbalance(SkCanvas* canvas) {
166 SkRect clipRect = SkRect::MakeWH(2, 2);
167 SkRect drawRect = SkRect::MakeWH(10, 10);
168 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500169 canvas->clipRect(clipRect, kReplace_SkClipOp);
robertphillips9058d602014-06-10 11:45:46 -0700170 canvas->translate(1.0f, 1.0f);
171 SkPaint p;
172 p.setColor(SK_ColorGREEN);
173 canvas->drawRect(drawRect, p);
174 // no restore
175}
176
177// This tests that replaying a potentially unbalanced picture into a canvas
178// doesn't affect the canvas' save count or matrix/clip state.
179static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
180 SkBitmap bm;
181 bm.allocN32Pixels(4, 3);
182 SkCanvas canvas(bm);
183
184 int beforeSaveCount = canvas.getSaveCount();
185
186 SkMatrix beforeMatrix = canvas.getTotalMatrix();
187
Mike Reed918e1442017-01-23 11:39:45 -0500188 SkRect beforeClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700189
190 canvas.drawPicture(picture);
191
192 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
193 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
194
Mike Reed918e1442017-01-23 11:39:45 -0500195 SkRect afterClip = canvas.getLocalClipBounds();
robertphillips9058d602014-06-10 11:45:46 -0700196
197 REPORTER_ASSERT(reporter, afterClip == beforeClip);
198}
199
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000200// Test out SkPictureRecorder::partialReplay
201DEF_TEST(PictureRecorder_replay, reporter) {
202 // check save/saveLayer state
203 {
204 SkPictureRecorder recorder;
205
robertphillips9f1c2412014-06-09 06:25:34 -0700206 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000207
halcanary96fcdcc2015-08-27 07:41:13 -0700208 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000209
reedca2622b2016-03-18 07:25:55 -0700210 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000211
212 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700213 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000214
halcanary96fcdcc2015-08-27 07:41:13 -0700215 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000216
reedca2622b2016-03-18 07:25:55 -0700217 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000218
reedca2622b2016-03-18 07:25:55 -0700219 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000220
221 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700222 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000223 }
224
robertphillips9058d602014-06-10 11:45:46 -0700225 // Recreate the Android partialReplay test case
226 {
227 SkPictureRecorder recorder;
228
halcanary96fcdcc2015-08-27 07:41:13 -0700229 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700230 create_imbalance(canvas);
231
232 int expectedSaveCount = canvas->getSaveCount();
233
reedca2622b2016-03-18 07:25:55 -0700234 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
235 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700236
237 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
238
239 // End the recording of source to test the picture finalization
240 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700241 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700242 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000243}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000244
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000245static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
246 SkCanvas testCanvas(100, 100);
247 set_canvas_to_save_count_4(&testCanvas);
248
249 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
250
251 SkPaint paint;
252 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
253
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000254 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000255
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000256 {
257 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700258 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000259 canvas->save();
260 canvas->translate(10, 10);
261 canvas->drawRect(rect, paint);
262 canvas->save();
263 canvas->translate(10, 10);
264 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700265 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000266
robertphillips9b14f262014-06-04 05:40:44 -0700267 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000268 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
269 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000270
271 set_canvas_to_save_count_4(&testCanvas);
272
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000273 {
274 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700275 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000276 canvas->save();
277 canvas->translate(10, 10);
278 canvas->drawRect(rect, paint);
279 canvas->save();
280 canvas->translate(10, 10);
281 canvas->drawRect(rect, paint);
282 canvas->restore();
283 canvas->restore();
284 canvas->restore();
285 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700286 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000287
robertphillips9b14f262014-06-04 05:40:44 -0700288 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000289 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
290 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000291
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000292 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000293
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000294 {
robertphillips9f1c2412014-06-09 06:25:34 -0700295 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000296 canvas->translate(10, 10);
297 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700298 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000299
robertphillips9b14f262014-06-04 05:40:44 -0700300 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000301 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
302 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
303 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000304}
305
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000306static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000307 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000308
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000309 SkPictureRecorder recorder;
310
reed@google.com21b519d2012-10-02 17:42:15 +0000311 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000312 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000313
robertphillips9f1c2412014-06-09 06:25:34 -0700314 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000315
316 for (int i = 0; i < 1000; ++i) {
317 rand_op(canvas, rand);
318 }
reedca2622b2016-03-18 07:25:55 -0700319 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000320
321 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000322 }
323
324 {
robertphillips9f1c2412014-06-09 06:25:34 -0700325 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000326 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000327
reed@google.com21b519d2012-10-02 17:42:15 +0000328 for (int i = 0; i < 100; ++i) {
329 canvas->save();
330 }
331 while (canvas->getSaveCount() > 1) {
332 canvas->clipRect(rect);
333 canvas->restore();
334 }
reedca2622b2016-03-18 07:25:55 -0700335 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000336 }
337}
338
scroggo@google.com4b90b112012-12-04 15:08:56 +0000339#ifndef SK_DEBUG
340// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
341// should never do this.
342static void test_bad_bitmap() {
343 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
344 // fail.
345 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000346 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000347 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700348 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000349 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700350 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000351
352 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700353 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000354}
355#endif
356
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000357static void test_clip_bound_opt(skiatest::Reporter* reporter) {
358 // Test for crbug.com/229011
359 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
360 SkIntToScalar(2), SkIntToScalar(2));
361 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
362 SkIntToScalar(1), SkIntToScalar(1));
363 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
364 SkIntToScalar(1), SkIntToScalar(1));
365
366 SkPath invPath;
367 invPath.addOval(rect1);
368 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
369 SkPath path;
370 path.addOval(rect2);
371 SkPath path2;
372 path2.addOval(rect3);
373 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000374 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700375
376 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000377 {
robertphillips9f1c2412014-06-09 06:25:34 -0700378 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700379 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500380 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000381 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
382 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
383 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
384 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
385 }
386 {
robertphillips9f1c2412014-06-09 06:25:34 -0700387 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700388 canvas->clipPath(path);
389 canvas->clipPath(invPath);
Mike Reed918e1442017-01-23 11:39:45 -0500390 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000391 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
392 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
393 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
394 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
395 }
396 {
robertphillips9f1c2412014-06-09 06:25:34 -0700397 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700398 canvas->clipPath(path);
Mike Reedc1f77742016-12-09 09:00:50 -0500399 canvas->clipPath(invPath, kUnion_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500400 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000401 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
402 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
403 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
404 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
405 }
406 {
robertphillips9f1c2412014-06-09 06:25:34 -0700407 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500408 canvas->clipPath(path, kDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500409 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000410 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
411 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
412 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
413 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
414 }
415 {
robertphillips9f1c2412014-06-09 06:25:34 -0700416 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500417 canvas->clipPath(path, kReverseDifference_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500418 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000419 // True clip is actually empty in this case, but the best
420 // determination we can make using only bounds as input is that the
421 // clip is included in the bounds of 'path'.
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000422 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
423 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
424 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
425 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
426 }
427 {
robertphillips9f1c2412014-06-09 06:25:34 -0700428 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500429 canvas->clipPath(path, kIntersect_SkClipOp);
430 canvas->clipPath(path2, kXOR_SkClipOp);
Mike Reed918e1442017-01-23 11:39:45 -0500431 clipBounds = canvas->getDeviceClipBounds();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000432 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
433 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
434 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
435 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
436 }
437}
438
schenneyeeff8bb2015-07-07 14:27:10 -0700439static void test_cull_rect_reset(skiatest::Reporter* reporter) {
440 SkPictureRecorder recorder;
441 SkRect bounds = SkRect::MakeWH(10, 10);
442 SkRTreeFactory factory;
443 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
444 bounds = SkRect::MakeWH(100, 100);
445 SkPaint paint;
446 canvas->drawRect(bounds, paint);
447 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700448 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
Mike Klein88d90712018-01-27 17:30:04 +0000449 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -0700450 REPORTER_ASSERT(reporter, picture);
451
452 SkRect finalCullRect = picture->cullRect();
453 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
454 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
455 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
456 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
457
458 const SkBBoxHierarchy* pictureBBH = picture->bbh();
459 SkRect bbhCullRect = pictureBBH->getRootBound();
460 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
461 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
462 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
463 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
464}
465
466
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000467/**
468 * A canvas that records the number of clip commands.
469 */
470class ClipCountingCanvas : public SkCanvas {
471public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000472 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000473 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000474 , fClipCount(0){
475 }
476
Mike Reedc1f77742016-12-09 09:00:50 -0500477 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000478 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000479 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000480 }
481
Mike Reedc1f77742016-12-09 09:00:50 -0500482 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000483 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000484 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000485 }
486
Mike Reedc1f77742016-12-09 09:00:50 -0500487 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000488 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000489 this->INHERITED::onClipPath(path, op, edgeStyle);
490 }
491
Mike Reedc1f77742016-12-09 09:00:50 -0500492 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000493 fClipCount += 1;
494 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000495 }
496
497 unsigned getClipCount() const { return fClipCount; }
498
499private:
500 unsigned fClipCount;
501
502 typedef SkCanvas INHERITED;
503};
504
505static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000506 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700507 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000508
Mike Reedc1f77742016-12-09 09:00:50 -0500509 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000510 // The following expanding clip should not be skipped.
Mike Reedc1f77742016-12-09 09:00:50 -0500511 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000512 // Draw something so the optimizer doesn't just fold the world.
513 SkPaint p;
514 p.setColor(SK_ColorBLUE);
515 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700516 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000517
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000518 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700519 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000520
521 // Both clips should be present on playback.
522 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
523}
524
robertphillips@google.comd5500882014-04-02 23:51:13 +0000525static void test_gen_id(skiatest::Reporter* reporter) {
526
Robert Phillipscfaeec42014-07-13 12:00:50 -0400527 SkPictureRecorder recorder;
528 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700529 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000530
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000531 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400532 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000533
robertphillips9f1c2412014-06-09 06:25:34 -0700534 SkCanvas* canvas = recorder.beginRecording(1, 1);
Mike Reed3661bc92017-02-22 13:21:42 -0500535 canvas->drawColor(SK_ColorWHITE);
reedca2622b2016-03-18 07:25:55 -0700536 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000537 // picture should have a non-zero id after recording
538 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000539
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000540 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400541 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000542}
543
caryclark5ef194c2015-08-31 09:22:38 -0700544static void test_typeface(skiatest::Reporter* reporter) {
545 SkPictureRecorder recorder;
546 SkCanvas* canvas = recorder.beginRecording(10, 10);
547 SkPaint paint;
Ben Wagner71319502017-07-27 10:45:29 -0400548 paint.setTypeface(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
Cary Clark2a475ea2017-04-28 15:35:12 -0400549 canvas->drawString("Q", 0, 10, paint);
reedca2622b2016-03-18 07:25:55 -0700550 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700551 SkDynamicMemoryWStream stream;
552 picture->serialize(&stream);
553}
554
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000555DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700556 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000557#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700558 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000559 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000560#else
561 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000562#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000563 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000564 test_peephole();
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000565 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000566 test_clip_expansion(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000567 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700568 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000569}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000570
571static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
572 const SkPaint paint;
573 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
574 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700575 int divs[] = { 2, 3 };
576 SkCanvas::Lattice lattice;
577 lattice.fXCount = lattice.fYCount = 2;
578 lattice.fXDivs = lattice.fYDivs = divs;
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);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000583 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -0800584 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -0700585 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000586}
587
588static void test_draw_bitmaps(SkCanvas* canvas) {
589 SkBitmap empty;
590 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000591 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000592 draw_bitmaps(empty, canvas);
593}
594
595DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000596 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700597 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700598 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000599}
600
601DEF_TEST(Canvas_EmptyBitmap, r) {
602 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000603 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000604 SkCanvas canvas(dst);
605
606 test_draw_bitmaps(&canvas);
607}
dneto3f22e8c2014-07-30 15:42:22 -0700608
609DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
610 // This test is from crbug.com/344987.
611 // The commands are:
612 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700613 // drawBitmapRect
614 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700615 // restore
616 // The bug was that this structure was modified so that:
617 // - The saveLayer and restore were eliminated
618 // - The alpha was only applied to the first drawBitmapRectToRect
619
620 // This test draws blue and red squares inside a 50% transparent
621 // layer. Both colours should show up muted.
622 // When the bug is present, the red square (the second bitmap)
623 // shows upwith full opacity.
624
625 SkBitmap blueBM;
626 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
627 SkBitmap redBM;
628 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
629 SkPaint semiTransparent;
630 semiTransparent.setAlpha(0x80);
631
632 SkPictureRecorder recorder;
633 SkCanvas* canvas = recorder.beginRecording(100, 100);
Mike Reed3661bc92017-02-22 13:21:42 -0500634 canvas->drawColor(0);
dneto3f22e8c2014-07-30 15:42:22 -0700635
Ben Wagnera93a14a2017-08-28 10:34:05 -0400636 canvas->saveLayer(nullptr, &semiTransparent);
dneto3f22e8c2014-07-30 15:42:22 -0700637 canvas->drawBitmap(blueBM, 25, 25);
638 canvas->drawBitmap(redBM, 50, 50);
639 canvas->restore();
640
reedca2622b2016-03-18 07:25:55 -0700641 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700642
643 // Now replay the picture back on another canvas
644 // and check a couple of its pixels.
645 SkBitmap replayBM;
646 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
647 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700648 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700649 replayCanvas.flush();
650
651 // With the bug present, at (55, 55) we would get a fully opaque red
652 // intead of a dark red.
653 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
654 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
655}
mtklein3e8232b2014-08-18 13:39:11 -0700656
657struct CountingBBH : public SkBBoxHierarchy {
658 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -0800659 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -0700660
schenney23d85932015-03-06 16:20:28 -0800661 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -0700662
mtkleinc6ad06a2015-08-19 09:51:00 -0700663 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700664 this->searchCalls++;
665 }
666
mtklein36352bf2015-03-25 18:17:31 -0700667 void insert(const SkRect[], int) override {}
668 virtual size_t bytesUsed() const override { return 0; }
669 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -0700670};
671
672class SpoonFedBBHFactory : public SkBBHFactory {
673public:
674 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -0700675 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700676 return SkRef(fBBH);
677 }
678private:
679 SkBBoxHierarchy* fBBH;
680};
681
682// When the canvas clip covers the full picture, we don't need to call the BBH.
683DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -0800684 SkRect bound = SkRect::MakeWH(320, 240);
685 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -0700686 SpoonFedBBHFactory factory(&bbh);
687
688 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -0700689 SkCanvas* c = recorder.beginRecording(bound, &factory);
690 // Record a few ops so we don't hit a small- or empty- picture optimization.
691 c->drawRect(bound, SkPaint());
692 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -0700693 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -0700694
695 SkCanvas big(640, 480), small(300, 200);
696
robertphillipsc5ba71d2014-09-04 08:42:50 -0700697 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -0700698 REPORTER_ASSERT(r, bbh.searchCalls == 0);
699
robertphillipsc5ba71d2014-09-04 08:42:50 -0700700 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -0700701 REPORTER_ASSERT(r, bbh.searchCalls == 1);
702}
mtkleind72094d2014-08-27 12:12:23 -0700703
704DEF_TEST(Picture_BitmapLeak, r) {
705 SkBitmap mut, immut;
706 mut.allocN32Pixels(300, 200);
707 immut.allocN32Pixels(300, 200);
708 immut.setImmutable();
709 SkASSERT(!mut.isImmutable());
710 SkASSERT(immut.isImmutable());
711
712 // No one can hold a ref on our pixels yet.
713 REPORTER_ASSERT(r, mut.pixelRef()->unique());
714 REPORTER_ASSERT(r, immut.pixelRef()->unique());
715
reedca2622b2016-03-18 07:25:55 -0700716 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -0800717 {
718 // we want the recorder to go out of scope before our subsequent checks, so we
719 // place it inside local braces.
720 SkPictureRecorder rec;
721 SkCanvas* canvas = rec.beginRecording(1920, 1200);
722 canvas->drawBitmap(mut, 0, 0);
723 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -0700724 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -0800725 }
mtkleind72094d2014-08-27 12:12:23 -0700726
727 // The picture shares the immutable pixels but copies the mutable ones.
728 REPORTER_ASSERT(r, mut.pixelRef()->unique());
729 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
730
731 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -0700732 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -0700733 REPORTER_ASSERT(r, mut.pixelRef()->unique());
734 REPORTER_ASSERT(r, immut.pixelRef()->unique());
735}
mtkleinfeaadee2015-04-08 11:25:48 -0700736
737// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
738DEF_TEST(Picture_getRecordingCanvas, r) {
739 SkPictureRecorder rec;
740 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
741 for (int i = 0; i < 3; i++) {
742 rec.beginRecording(100, 100);
743 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -0700744 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -0700745 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
746 }
747}
mtklein9db912c2015-05-19 11:11:26 -0700748
749DEF_TEST(MiniRecorderLeftHanging, r) {
750 // Any shader or other ref-counted effect will do just fine here.
751 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -0700752 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
mtklein9db912c2015-05-19 11:11:26 -0700753
754 SkMiniRecorder rec;
755 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
756 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
757}
fmalita2ecc0002015-07-14 13:12:25 -0700758
759DEF_TEST(Picture_preserveCullRect, r) {
760 SkPictureRecorder recorder;
761
762 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
763 c->clear(SK_ColorCYAN);
764
reedca2622b2016-03-18 07:25:55 -0700765 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -0700766 SkDynamicMemoryWStream wstream;
767 picture->serialize(&wstream);
768
Ben Wagner145dbcd2016-11-03 14:40:50 -0400769 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
770 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -0700771
mtklein5f939ab2016-03-16 10:28:35 -0700772 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -0700773 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
774 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
775 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
776 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
777}
fmalita796e3652016-05-13 11:40:07 -0700778
Mike Klein26eb16f2017-04-10 09:50:25 -0400779
780// If we record bounded ops into a picture with a big cull and calculate the
781// bounds of those ops, we should trim down the picture cull to the ops' bounds.
782// If we're not using an SkBBH, we shouldn't change it.
783DEF_TEST(Picture_UpdatedCull_1, r) {
784 // Testing 1 draw exercises SkMiniPicture.
785 SkRTreeFactory factory;
786 SkPictureRecorder recorder;
787
Mike Reed274218e2018-01-08 15:05:02 -0500788 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400789 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
790 auto pic = recorder.finishRecordingAsPicture();
791 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
792
Mike Reed274218e2018-01-08 15:05:02 -0500793 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400794 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
795 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500796 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400797}
798DEF_TEST(Picture_UpdatedCull_2, r) {
799 // Testing >1 draw exercises SkBigPicture.
800 SkRTreeFactory factory;
801 SkPictureRecorder recorder;
802
Mike Reed274218e2018-01-08 15:05:02 -0500803 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
Mike Klein26eb16f2017-04-10 09:50:25 -0400804 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
805 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
806 auto pic = recorder.finishRecordingAsPicture();
807 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
808
Mike Reed274218e2018-01-08 15:05:02 -0500809 canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400810 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
811 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
812 pic = recorder.finishRecordingAsPicture();
Mike Reed274218e2018-01-08 15:05:02 -0500813 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
Mike Klein26eb16f2017-04-10 09:50:25 -0400814}
Mike Klein7cc49d62017-08-14 10:39:28 -0400815
Mike Klein88d90712018-01-27 17:30:04 +0000816DEF_TEST(Picture_RecordsFlush, r) {
817 SkPictureRecorder recorder;
818
819 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
820 for (int i = 0; i < 10; i++) {
821 canvas->clear(0);
822 for (int j = 0; j < 10; j++) {
823 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
824 }
825 canvas->flush();
826 }
827
828 // Did we record the flushes?
829 auto pic = recorder.finishRecordingAsPicture();
830 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
831
832 // Do we serialize and deserialize flushes?
833 auto skp = pic->serialize();
834 auto back = SkPicture::MakeFromData(skp->data(), skp->size());
835 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
836}
Mike Kleinfbe66202018-01-26 09:49:48 -0500837
838DEF_TEST(Placeholder, r) {
839 SkRect cull = { 0,0, 10,20 };
840
841 // Each placeholder is unique.
842 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
843 p2 = SkPicture::MakePlaceholder(cull);
844 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
845 REPORTER_ASSERT(r, p1->cullRect() == cull);
846 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
847
848 // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
849 SkPictureRecorder recorder;
850 SkCanvas* canvas = recorder.beginRecording(cull);
851 canvas->drawPicture(p1);
852 canvas->drawPicture(p2);
853 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
854 REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
855}
Mike Reed40d82972018-02-01 14:45:50 -0500856
857DEF_TEST(Picture_empty_serial, reporter) {
858 SkPictureRecorder rec;
859 (void)rec.beginRecording(10, 10);
860 auto pic = rec.finishRecordingAsPicture();
861 REPORTER_ASSERT(reporter, pic);
862
863 auto data = pic->serialize();
864 REPORTER_ASSERT(reporter, data);
865
866 auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
867 REPORTER_ASSERT(reporter, pic2);
868}
869