blob: d47165eaf2c4952c2446c8b580ccfce67c7218da [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"
reed@google.com21b519d2012-10-02 17:42:15 +000020#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000021#include "SkPicture.h"
fmalita796e3652016-05-13 11:40:07 -070022#include "SkPictureAnalyzer.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000023#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000024#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070025#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080026#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070027#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000028#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000029#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080030#include "SkRecord.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"
robertphillips3e5c2b12015-03-23 05:46:51 -070033#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000034
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000035#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000036
reed@google.com47b679b2014-05-14 18:58:16 +000037#include "SkLumaColorFilter.h"
38#include "SkColorFilterImageFilter.h"
39
reed@google.comfe7b1ed2012-11-29 21:00:39 +000040static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000041 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000042 bm->eraseColor(color);
43 if (immutable) {
44 bm->setImmutable();
45 }
46}
47
mtkleina16af212015-08-26 08:14:52 -070048// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
49static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
reede3b38ce2016-01-08 09:18:44 -080050 // We just need _some_ SkImage
51 const SkPMColor pixel = 0;
52 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
reed9ce9d672016-03-17 10:51:11 -070053 sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel))));
mtkleina16af212015-08-26 08:14:52 -070054
55 SkPictureRecorder recorder;
reede3b38ce2016-01-08 09:18:44 -080056 recorder.beginRecording(100,100)->drawImage(image, 0,0);
reedca2622b2016-03-18 07:25:55 -070057 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtkleina16af212015-08-26 08:14:52 -070058
59 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
60}
61
tomhudson3a0f2792014-08-20 05:29:41 -070062/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070063static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070064 SkPictureRecorder recorder;
65
mtklein8e126562014-10-01 09:29:35 -070066 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070067 {
68 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
69 }
reedca2622b2016-03-18 07:25:55 -070070 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
tomhudson3a0f2792014-08-20 05:29:41 -070071 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
72
mtklein8e126562014-10-01 09:29:35 -070073 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070074 {
75 SkPaint paint;
76 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
77 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
78 SkBitmap bitmap;
79 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
80 bitmap.eraseColor(SK_ColorBLUE);
81 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
reed1a9b9642016-03-13 14:13:58 -070082 paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
83 SkShader::kClamp_TileMode));
Mike Reed627778d2016-09-28 17:13:38 -040084 REPORTER_ASSERT(reporter, paint.getShader()->isAImage());
tomhudson3a0f2792014-08-20 05:29:41 -070085
86 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
87 }
reedca2622b2016-03-18 07:25:55 -070088 REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps());
tomhudson3a0f2792014-08-20 05:29:41 -070089}
90
91
scroggo@google.comd614c6a2012-09-14 17:26:37 +000092#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070093// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070094// in debug mode, so only run in debug mode.
95static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000096 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000097 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070098 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070099 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -0700100 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -0700101 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700102 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000103}
104
105// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
106static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000107 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700108 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700109 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000110 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000111 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000112}
113#endif
114
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000115static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000116 SkPaint paint;
117 SkRect rect = SkRect::MakeWH(50, 50);
118
119 SkScalar unit = rand.nextUScalar1();
120 if (unit <= 0.3) {
121// SkDebugf("save\n");
122 canvas->save();
123 } else if (unit <= 0.6) {
124// SkDebugf("restore\n");
125 canvas->restore();
126 } else if (unit <= 0.9) {
127// SkDebugf("clip\n");
128 canvas->clipRect(rect);
129 } else {
130// SkDebugf("draw\n");
131 canvas->drawPaint(paint);
132 }
133}
134
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000135#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700136
fmalitab5fc58e2016-05-25 11:31:04 -0700137static SkPath make_convex_path() {
138 SkPath path;
139 path.lineTo(100, 0);
140 path.lineTo(50, 100);
141 path.close();
142
143 return path;
144}
145
146static SkPath make_concave_path() {
147 SkPath path;
148 path.lineTo(50, 50);
149 path.lineTo(100, 0);
150 path.lineTo(50, 100);
151 path.close();
152
153 return path;
154}
155
mtklein8e126562014-10-01 09:29:35 -0700156static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000157 SkPictureRecorder recorder;
158
mtklein8e126562014-10-01 09:29:35 -0700159 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000160 {
161 SkPath path;
162 path.moveTo(0, 0);
163 path.lineTo(50, 50);
164
165 SkScalar intervals[] = { 1.0f, 1.0f };
reeda4393342016-03-18 11:22:57 -0700166 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000167
168 SkPaint paint;
169 paint.setStyle(SkPaint::kStroke_Style);
170 paint.setPathEffect(dash);
171
robertphillips98b03152015-01-26 11:29:36 -0800172 for (int i = 0; i < 50; ++i) {
173 canvas->drawPath(path, paint);
174 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000175 }
reedca2622b2016-03-18 07:25:55 -0700176 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000177 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000178
halcanary96fcdcc2015-08-27 07:41:13 -0700179 const char *reason = nullptr;
fmalita796e3652016-05-13 11:40:07 -0700180 REPORTER_ASSERT(reporter,
181 !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason));
bsalomon49f085d2014-09-05 13:34:00 -0700182 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000183
mtklein8e126562014-10-01 09:29:35 -0700184 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000185 {
186 SkPath path;
187
188 path.moveTo(0, 0);
189 path.lineTo(0, 50);
190 path.lineTo(25, 25);
191 path.lineTo(50, 50);
192 path.lineTo(50, 0);
193 path.close();
194 REPORTER_ASSERT(reporter, !path.isConvex());
195
196 SkPaint paint;
197 paint.setAntiAlias(true);
198 for (int i = 0; i < 50; ++i) {
199 canvas->drawPath(path, paint);
200 }
201 }
reedca2622b2016-03-18 07:25:55 -0700202 picture = recorder.finishRecordingAsPicture();
jvanverthd86b07a2014-11-04 08:50:15 -0800203 // A lot of small AA concave paths should be fine for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700204 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
jvanverthd86b07a2014-11-04 08:50:15 -0800205
206 canvas = recorder.beginRecording(100, 100);
207 {
208 SkPath path;
209
210 path.moveTo(0, 0);
211 path.lineTo(0, 100);
212 path.lineTo(50, 50);
213 path.lineTo(100, 100);
214 path.lineTo(100, 0);
215 path.close();
216 REPORTER_ASSERT(reporter, !path.isConvex());
217
218 SkPaint paint;
219 paint.setAntiAlias(true);
220 for (int i = 0; i < 50; ++i) {
221 canvas->drawPath(path, paint);
222 }
223 }
reedca2622b2016-03-18 07:25:55 -0700224 picture = recorder.finishRecordingAsPicture();
jvanverthd86b07a2014-11-04 08:50:15 -0800225 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700226 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000227
mtklein8e126562014-10-01 09:29:35 -0700228 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000229 {
230 SkPath path;
231
232 path.moveTo(0, 0);
233 path.lineTo(0, 50);
234 path.lineTo(25, 25);
235 path.lineTo(50, 50);
236 path.lineTo(50, 0);
237 path.close();
238 REPORTER_ASSERT(reporter, !path.isConvex());
239
240 SkPaint paint;
241 paint.setAntiAlias(true);
242 paint.setStyle(SkPaint::kStroke_Style);
243 paint.setStrokeWidth(0);
244 for (int i = 0; i < 50; ++i) {
245 canvas->drawPath(path, paint);
246 }
247 }
reedca2622b2016-03-18 07:25:55 -0700248 picture = recorder.finishRecordingAsPicture();
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000249 // hairline stroked AA concave paths are fine for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700250 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
tomhudson3a0f2792014-08-20 05:29:41 -0700251
mtklein8e126562014-10-01 09:29:35 -0700252 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700253 {
254 SkPaint paint;
255 SkScalar intervals [] = { 10, 20 };
reeda4393342016-03-18 11:22:57 -0700256 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
tomhudson3a0f2792014-08-20 05:29:41 -0700257
258 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800259
260 for (int i = 0; i < 50; ++i) {
261 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
262 }
tomhudson3a0f2792014-08-20 05:29:41 -0700263 }
reedca2622b2016-03-18 07:25:55 -0700264 picture = recorder.finishRecordingAsPicture();
tomhudson3a0f2792014-08-20 05:29:41 -0700265 // fast-path dashed effects are fine for GPU rendering ...
fmalita796e3652016-05-13 11:40:07 -0700266 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
tomhudson3a0f2792014-08-20 05:29:41 -0700267
mtklein8e126562014-10-01 09:29:35 -0700268 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700269 {
270 SkPaint paint;
271 SkScalar intervals [] = { 10, 20 };
reeda4393342016-03-18 11:22:57 -0700272 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
tomhudson3a0f2792014-08-20 05:29:41 -0700273
robertphillips98b03152015-01-26 11:29:36 -0800274 for (int i = 0; i < 50; ++i) {
275 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
276 }
tomhudson3a0f2792014-08-20 05:29:41 -0700277 }
reedca2622b2016-03-18 07:25:55 -0700278 picture = recorder.finishRecordingAsPicture();
tomhudson3a0f2792014-08-20 05:29:41 -0700279 // ... but only when applied to drawPoint() calls
fmalita796e3652016-05-13 11:40:07 -0700280 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
mtklein53fecfb2014-08-21 09:11:37 -0700281
fmalitab5fc58e2016-05-25 11:31:04 -0700282 canvas = recorder.beginRecording(100, 100);
283 {
284 const SkPath convexClip = make_convex_path();
285 const SkPath concaveClip = make_concave_path();
286
287 for (int i = 0; i < 50; ++i) {
288 canvas->clipPath(convexClip);
289 canvas->clipPath(concaveClip);
Mike Reedc1f77742016-12-09 09:00:50 -0500290 canvas->clipPath(convexClip, kIntersect_SkClipOp, true);
fmalitab5fc58e2016-05-25 11:31:04 -0700291 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
292 }
293 }
294 picture = recorder.finishRecordingAsPicture();
295 // Convex clips and non-AA concave clips are fine on the GPU.
296 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
297
298 canvas = recorder.beginRecording(100, 100);
299 {
300 const SkPath concaveClip = make_concave_path();
301 for (int i = 0; i < 50; ++i) {
Mike Reedc1f77742016-12-09 09:00:50 -0500302 canvas->clipPath(concaveClip, kIntersect_SkClipOp, true);
fmalitab5fc58e2016-05-25 11:31:04 -0700303 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
304 }
305 }
306 picture = recorder.finishRecordingAsPicture();
307 // ... but AA concave clips are not.
308 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
309
mtklein53fecfb2014-08-21 09:11:37 -0700310 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700311 canvas = recorder.beginRecording(100, 100);
312 {
fmalita796e3652016-05-13 11:40:07 -0700313 canvas->drawPicture(picture);
mtklein53fecfb2014-08-21 09:11:37 -0700314 }
reedca2622b2016-03-18 07:25:55 -0700315 picture = recorder.finishRecordingAsPicture();
fmalita796e3652016-05-13 11:40:07 -0700316 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000317}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000318
fmalita796e3652016-05-13 11:40:07 -0700319#endif // SK_SUPPORT_GPU
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000320
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000321static void set_canvas_to_save_count_4(SkCanvas* canvas) {
322 canvas->restoreToCount(1);
323 canvas->save();
324 canvas->save();
325 canvas->save();
326}
327
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000328/**
329 * A canvas that records the number of saves, saveLayers and restores.
330 */
331class SaveCountingCanvas : public SkCanvas {
332public:
333 SaveCountingCanvas(int width, int height)
334 : INHERITED(width, height)
335 , fSaveCount(0)
336 , fSaveLayerCount(0)
337 , fRestoreCount(0){
338 }
339
reed4960eee2015-12-18 07:09:18 -0800340 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000341 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800342 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000343 }
344
mtklein36352bf2015-03-25 18:17:31 -0700345 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000346 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400347 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000348 }
349
mtklein36352bf2015-03-25 18:17:31 -0700350 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000351 ++fRestoreCount;
352 this->INHERITED::willRestore();
353 }
354
355 unsigned int getSaveCount() const { return fSaveCount; }
356 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
357 unsigned int getRestoreCount() const { return fRestoreCount; }
358
359private:
360 unsigned int fSaveCount;
361 unsigned int fSaveLayerCount;
362 unsigned int fRestoreCount;
363
364 typedef SkCanvas INHERITED;
365};
366
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000367void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000368 unsigned int numSaves, unsigned int numSaveLayers,
369 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700370 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700371 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000372
robertphillipsc5ba71d2014-09-04 08:42:50 -0700373 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000374
mtklein87c41382014-09-08 07:31:18 -0700375 // Optimizations may have removed these,
376 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
377 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
378 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
379 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000380}
381
382// This class exists so SkPicture can friend it and give it access to
383// the 'partialReplay' method.
384class SkPictureRecorderReplayTester {
385public:
reedca2622b2016-03-18 07:25:55 -0700386 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000387 SkPictureRecorder recorder2;
388
robertphillips9f1c2412014-06-09 06:25:34 -0700389 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000390
391 recorder->partialReplay(canvas);
392
reedca2622b2016-03-18 07:25:55 -0700393 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000394 }
395};
396
robertphillips9058d602014-06-10 11:45:46 -0700397static void create_imbalance(SkCanvas* canvas) {
398 SkRect clipRect = SkRect::MakeWH(2, 2);
399 SkRect drawRect = SkRect::MakeWH(10, 10);
400 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500401 canvas->clipRect(clipRect, kReplace_SkClipOp);
robertphillips9058d602014-06-10 11:45:46 -0700402 canvas->translate(1.0f, 1.0f);
403 SkPaint p;
404 p.setColor(SK_ColorGREEN);
405 canvas->drawRect(drawRect, p);
406 // no restore
407}
408
409// This tests that replaying a potentially unbalanced picture into a canvas
410// doesn't affect the canvas' save count or matrix/clip state.
411static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
412 SkBitmap bm;
413 bm.allocN32Pixels(4, 3);
414 SkCanvas canvas(bm);
415
416 int beforeSaveCount = canvas.getSaveCount();
417
418 SkMatrix beforeMatrix = canvas.getTotalMatrix();
419
420 SkRect beforeClip;
421
422 canvas.getClipBounds(&beforeClip);
423
424 canvas.drawPicture(picture);
425
426 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
427 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
428
429 SkRect afterClip;
430
431 canvas.getClipBounds(&afterClip);
432
433 REPORTER_ASSERT(reporter, afterClip == beforeClip);
434}
435
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000436// Test out SkPictureRecorder::partialReplay
437DEF_TEST(PictureRecorder_replay, reporter) {
438 // check save/saveLayer state
439 {
440 SkPictureRecorder recorder;
441
robertphillips9f1c2412014-06-09 06:25:34 -0700442 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000443
halcanary96fcdcc2015-08-27 07:41:13 -0700444 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000445
reedca2622b2016-03-18 07:25:55 -0700446 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000447
448 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700449 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000450
halcanary96fcdcc2015-08-27 07:41:13 -0700451 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000452
reedca2622b2016-03-18 07:25:55 -0700453 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000454
reedca2622b2016-03-18 07:25:55 -0700455 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000456
457 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700458 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000459 }
460
461 // (partially) check leakage of draw ops
462 {
463 SkPictureRecorder recorder;
464
robertphillips9f1c2412014-06-09 06:25:34 -0700465 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000466
467 SkRect r = SkRect::MakeWH(5, 5);
468 SkPaint p;
469
470 canvas->drawRect(r, p);
471
reedca2622b2016-03-18 07:25:55 -0700472 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000473
474 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
475
476 SkBitmap bm;
477 make_bm(&bm, 10, 10, SK_ColorRED, true);
478
479 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700480 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000481
reedca2622b2016-03-18 07:25:55 -0700482 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000483 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
484
485 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
486
487 // The snapshot shouldn't pick up any operations added after it was made
488 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
489 }
robertphillips9058d602014-06-10 11:45:46 -0700490
491 // Recreate the Android partialReplay test case
492 {
493 SkPictureRecorder recorder;
494
halcanary96fcdcc2015-08-27 07:41:13 -0700495 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700496 create_imbalance(canvas);
497
498 int expectedSaveCount = canvas->getSaveCount();
499
reedca2622b2016-03-18 07:25:55 -0700500 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
501 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700502
503 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
504
505 // End the recording of source to test the picture finalization
506 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700507 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700508 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000509}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000510
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000511static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
512 SkCanvas testCanvas(100, 100);
513 set_canvas_to_save_count_4(&testCanvas);
514
515 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
516
517 SkPaint paint;
518 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
519
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000520 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000521
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000522 {
523 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700524 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000525 canvas->save();
526 canvas->translate(10, 10);
527 canvas->drawRect(rect, paint);
528 canvas->save();
529 canvas->translate(10, 10);
530 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700531 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000532
robertphillips9b14f262014-06-04 05:40:44 -0700533 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000534 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
535 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000536
537 set_canvas_to_save_count_4(&testCanvas);
538
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000539 {
540 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700541 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000542 canvas->save();
543 canvas->translate(10, 10);
544 canvas->drawRect(rect, paint);
545 canvas->save();
546 canvas->translate(10, 10);
547 canvas->drawRect(rect, paint);
548 canvas->restore();
549 canvas->restore();
550 canvas->restore();
551 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700552 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000553
robertphillips9b14f262014-06-04 05:40:44 -0700554 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000555 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
556 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000557
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000558 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000559
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000560 {
robertphillips9f1c2412014-06-09 06:25:34 -0700561 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000562 canvas->translate(10, 10);
563 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700564 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000565
robertphillips9b14f262014-06-04 05:40:44 -0700566 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000567 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
568 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
569 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000570}
571
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000572static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000573 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000574
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000575 SkPictureRecorder recorder;
576
reed@google.com21b519d2012-10-02 17:42:15 +0000577 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000578 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000579
robertphillips9f1c2412014-06-09 06:25:34 -0700580 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000581
582 for (int i = 0; i < 1000; ++i) {
583 rand_op(canvas, rand);
584 }
reedca2622b2016-03-18 07:25:55 -0700585 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000586
587 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000588 }
589
590 {
robertphillips9f1c2412014-06-09 06:25:34 -0700591 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000592 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000593
reed@google.com21b519d2012-10-02 17:42:15 +0000594 for (int i = 0; i < 100; ++i) {
595 canvas->save();
596 }
597 while (canvas->getSaveCount() > 1) {
598 canvas->clipRect(rect);
599 canvas->restore();
600 }
reedca2622b2016-03-18 07:25:55 -0700601 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000602 }
603}
604
scroggo@google.com4b90b112012-12-04 15:08:56 +0000605#ifndef SK_DEBUG
606// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
607// should never do this.
608static void test_bad_bitmap() {
609 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
610 // fail.
611 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000612 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000613 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700614 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000615 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700616 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000617
618 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700619 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000620}
621#endif
622
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000623static void test_clip_bound_opt(skiatest::Reporter* reporter) {
624 // Test for crbug.com/229011
625 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
626 SkIntToScalar(2), SkIntToScalar(2));
627 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
628 SkIntToScalar(1), SkIntToScalar(1));
629 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
630 SkIntToScalar(1), SkIntToScalar(1));
631
632 SkPath invPath;
633 invPath.addOval(rect1);
634 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
635 SkPath path;
636 path.addOval(rect2);
637 SkPath path2;
638 path2.addOval(rect3);
639 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000640 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700641
642 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000643 {
robertphillips9f1c2412014-06-09 06:25:34 -0700644 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700645 canvas->clipPath(invPath);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000646 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
647 REPORTER_ASSERT(reporter, true == nonEmpty);
648 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
649 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
650 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
651 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
652 }
653 {
robertphillips9f1c2412014-06-09 06:25:34 -0700654 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700655 canvas->clipPath(path);
656 canvas->clipPath(invPath);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000657 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
658 REPORTER_ASSERT(reporter, true == nonEmpty);
659 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
660 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
661 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
662 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
663 }
664 {
robertphillips9f1c2412014-06-09 06:25:34 -0700665 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700666 canvas->clipPath(path);
Mike Reedc1f77742016-12-09 09:00:50 -0500667 canvas->clipPath(invPath, kUnion_SkClipOp);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000668 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
669 REPORTER_ASSERT(reporter, true == nonEmpty);
670 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
671 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
672 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
673 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
674 }
675 {
robertphillips9f1c2412014-06-09 06:25:34 -0700676 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500677 canvas->clipPath(path, kDifference_SkClipOp);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000678 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
679 REPORTER_ASSERT(reporter, true == nonEmpty);
680 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
681 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
682 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
683 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
684 }
685 {
robertphillips9f1c2412014-06-09 06:25:34 -0700686 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500687 canvas->clipPath(path, kReverseDifference_SkClipOp);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000688 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
689 // True clip is actually empty in this case, but the best
690 // determination we can make using only bounds as input is that the
691 // clip is included in the bounds of 'path'.
692 REPORTER_ASSERT(reporter, true == nonEmpty);
693 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
694 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
695 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
696 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
697 }
698 {
robertphillips9f1c2412014-06-09 06:25:34 -0700699 SkCanvas* canvas = recorder.beginRecording(10, 10);
Mike Reedc1f77742016-12-09 09:00:50 -0500700 canvas->clipPath(path, kIntersect_SkClipOp);
701 canvas->clipPath(path2, kXOR_SkClipOp);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000702 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
703 REPORTER_ASSERT(reporter, true == nonEmpty);
704 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
705 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
706 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
707 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
708 }
709}
710
schenneyeeff8bb2015-07-07 14:27:10 -0700711static void test_cull_rect_reset(skiatest::Reporter* reporter) {
712 SkPictureRecorder recorder;
713 SkRect bounds = SkRect::MakeWH(10, 10);
714 SkRTreeFactory factory;
715 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
716 bounds = SkRect::MakeWH(100, 100);
717 SkPaint paint;
718 canvas->drawRect(bounds, paint);
719 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700720 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
mtkleineedc3342015-07-08 08:26:39 -0700721 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -0700722 REPORTER_ASSERT(reporter, picture);
723
724 SkRect finalCullRect = picture->cullRect();
725 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
726 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
727 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
728 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
729
730 const SkBBoxHierarchy* pictureBBH = picture->bbh();
731 SkRect bbhCullRect = pictureBBH->getRootBound();
732 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
733 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
734 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
735 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
736}
737
738
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000739/**
740 * A canvas that records the number of clip commands.
741 */
742class ClipCountingCanvas : public SkCanvas {
743public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000744 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000745 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000746 , fClipCount(0){
747 }
748
Mike Reedc1f77742016-12-09 09:00:50 -0500749 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000750 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000751 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000752 }
753
Mike Reedc1f77742016-12-09 09:00:50 -0500754 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000755 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000756 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000757 }
758
Mike Reedc1f77742016-12-09 09:00:50 -0500759 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000760 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000761 this->INHERITED::onClipPath(path, op, edgeStyle);
762 }
763
Mike Reedc1f77742016-12-09 09:00:50 -0500764 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000765 fClipCount += 1;
766 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000767 }
768
769 unsigned getClipCount() const { return fClipCount; }
770
771private:
772 unsigned fClipCount;
773
774 typedef SkCanvas INHERITED;
775};
776
777static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000778 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700779 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000780
Mike Reedc1f77742016-12-09 09:00:50 -0500781 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000782 // The following expanding clip should not be skipped.
Mike Reedc1f77742016-12-09 09:00:50 -0500783 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000784 // Draw something so the optimizer doesn't just fold the world.
785 SkPaint p;
786 p.setColor(SK_ColorBLUE);
787 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700788 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000789
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000790 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700791 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000792
793 // Both clips should be present on playback.
794 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
795}
796
tomhudson@google.com381010e2013-10-24 11:12:47 +0000797static void test_hierarchical(skiatest::Reporter* reporter) {
798 SkBitmap bm;
799 make_bm(&bm, 10, 10, SK_ColorRED, true);
800
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000801 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000802
robertphillips9f1c2412014-06-09 06:25:34 -0700803 recorder.beginRecording(10, 10);
reedca2622b2016-03-18 07:25:55 -0700804 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000805 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +0000806
robertphillips9f1c2412014-06-09 06:25:34 -0700807 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700808 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000809 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +0000810
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000811 {
robertphillips9f1c2412014-06-09 06:25:34 -0700812 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700813 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700814 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000815 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
816 }
817 {
robertphillips9f1c2412014-06-09 06:25:34 -0700818 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700819 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700820 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000821 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
822 }
823 {
robertphillips9f1c2412014-06-09 06:25:34 -0700824 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000825 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700826 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700827 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000828 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
829 }
830 {
robertphillips9f1c2412014-06-09 06:25:34 -0700831 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000832 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700833 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700834 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000835 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
836 }
tomhudson@google.com381010e2013-10-24 11:12:47 +0000837}
838
robertphillips@google.comd5500882014-04-02 23:51:13 +0000839static void test_gen_id(skiatest::Reporter* reporter) {
840
Robert Phillipscfaeec42014-07-13 12:00:50 -0400841 SkPictureRecorder recorder;
842 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700843 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000844
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000845 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400846 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000847
robertphillips9f1c2412014-06-09 06:25:34 -0700848 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000849 canvas->drawARGB(255, 255, 255, 255);
reedca2622b2016-03-18 07:25:55 -0700850 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000851 // picture should have a non-zero id after recording
852 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000853
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000854 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400855 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000856}
857
caryclark5ef194c2015-08-31 09:22:38 -0700858static void test_typeface(skiatest::Reporter* reporter) {
859 SkPictureRecorder recorder;
860 SkCanvas* canvas = recorder.beginRecording(10, 10);
861 SkPaint paint;
mbocee6a9912016-05-31 11:42:36 -0700862 paint.setTypeface(SkTypeface::MakeFromName("Arial",
863 SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
caryclark5ef194c2015-08-31 09:22:38 -0700864 canvas->drawText("Q", 1, 0, 10, paint);
reedca2622b2016-03-18 07:25:55 -0700865 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700866 SkDynamicMemoryWStream stream;
867 picture->serialize(&stream);
868}
869
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000870DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700871 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000872#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700873 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000874 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000875#else
876 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000877#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000878 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000879 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000880#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -0700881 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000882#endif
mtkleina16af212015-08-26 08:14:52 -0700883 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -0700884 test_analysis(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000885 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000886 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000887 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000888 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700889 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000890}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000891
892static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
893 const SkPaint paint;
894 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
895 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700896 int divs[] = { 2, 3 };
897 SkCanvas::Lattice lattice;
898 lattice.fXCount = lattice.fYCount = 2;
899 lattice.fXDivs = lattice.fYDivs = divs;
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000900
901 // Don't care what these record, as long as they're legal.
902 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -0700903 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000904 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -0800905 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -0700906 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000907}
908
909static void test_draw_bitmaps(SkCanvas* canvas) {
910 SkBitmap empty;
911 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000912 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000913 draw_bitmaps(empty, canvas);
914}
915
916DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000917 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700918 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -0700919 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000920}
921
922DEF_TEST(Canvas_EmptyBitmap, r) {
923 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000924 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000925 SkCanvas canvas(dst);
926
927 test_draw_bitmaps(&canvas);
928}
dneto3f22e8c2014-07-30 15:42:22 -0700929
930DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
931 // This test is from crbug.com/344987.
932 // The commands are:
933 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -0700934 // drawBitmapRect
935 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -0700936 // restore
937 // The bug was that this structure was modified so that:
938 // - The saveLayer and restore were eliminated
939 // - The alpha was only applied to the first drawBitmapRectToRect
940
941 // This test draws blue and red squares inside a 50% transparent
942 // layer. Both colours should show up muted.
943 // When the bug is present, the red square (the second bitmap)
944 // shows upwith full opacity.
945
946 SkBitmap blueBM;
947 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
948 SkBitmap redBM;
949 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
950 SkPaint semiTransparent;
951 semiTransparent.setAlpha(0x80);
952
953 SkPictureRecorder recorder;
954 SkCanvas* canvas = recorder.beginRecording(100, 100);
955 canvas->drawARGB(0, 0, 0, 0);
956
957 canvas->saveLayer(0, &semiTransparent);
958 canvas->drawBitmap(blueBM, 25, 25);
959 canvas->drawBitmap(redBM, 50, 50);
960 canvas->restore();
961
reedca2622b2016-03-18 07:25:55 -0700962 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -0700963
964 // Now replay the picture back on another canvas
965 // and check a couple of its pixels.
966 SkBitmap replayBM;
967 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
968 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700969 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -0700970 replayCanvas.flush();
971
972 // With the bug present, at (55, 55) we would get a fully opaque red
973 // intead of a dark red.
974 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
975 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
976}
mtklein3e8232b2014-08-18 13:39:11 -0700977
978struct CountingBBH : public SkBBoxHierarchy {
979 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -0800980 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -0700981
schenney23d85932015-03-06 16:20:28 -0800982 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -0700983
mtkleinc6ad06a2015-08-19 09:51:00 -0700984 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700985 this->searchCalls++;
986 }
987
mtklein36352bf2015-03-25 18:17:31 -0700988 void insert(const SkRect[], int) override {}
989 virtual size_t bytesUsed() const override { return 0; }
990 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -0700991};
992
993class SpoonFedBBHFactory : public SkBBHFactory {
994public:
995 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -0700996 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -0700997 return SkRef(fBBH);
998 }
999private:
1000 SkBBoxHierarchy* fBBH;
1001};
1002
1003// When the canvas clip covers the full picture, we don't need to call the BBH.
1004DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001005 SkRect bound = SkRect::MakeWH(320, 240);
1006 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001007 SpoonFedBBHFactory factory(&bbh);
1008
1009 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001010 SkCanvas* c = recorder.beginRecording(bound, &factory);
1011 // Record a few ops so we don't hit a small- or empty- picture optimization.
1012 c->drawRect(bound, SkPaint());
1013 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -07001014 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -07001015
1016 SkCanvas big(640, 480), small(300, 200);
1017
robertphillipsc5ba71d2014-09-04 08:42:50 -07001018 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001019 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1020
robertphillipsc5ba71d2014-09-04 08:42:50 -07001021 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001022 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1023}
mtkleind72094d2014-08-27 12:12:23 -07001024
1025DEF_TEST(Picture_BitmapLeak, r) {
1026 SkBitmap mut, immut;
1027 mut.allocN32Pixels(300, 200);
1028 immut.allocN32Pixels(300, 200);
1029 immut.setImmutable();
1030 SkASSERT(!mut.isImmutable());
1031 SkASSERT(immut.isImmutable());
1032
1033 // No one can hold a ref on our pixels yet.
1034 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1035 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1036
reedca2622b2016-03-18 07:25:55 -07001037 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -08001038 {
1039 // we want the recorder to go out of scope before our subsequent checks, so we
1040 // place it inside local braces.
1041 SkPictureRecorder rec;
1042 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1043 canvas->drawBitmap(mut, 0, 0);
1044 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -07001045 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -08001046 }
mtkleind72094d2014-08-27 12:12:23 -07001047
1048 // The picture shares the immutable pixels but copies the mutable ones.
1049 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1050 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1051
1052 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -07001053 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -07001054 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1055 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1056}
mtkleinfeaadee2015-04-08 11:25:48 -07001057
1058// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1059DEF_TEST(Picture_getRecordingCanvas, r) {
1060 SkPictureRecorder rec;
1061 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1062 for (int i = 0; i < 3; i++) {
1063 rec.beginRecording(100, 100);
1064 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -07001065 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -07001066 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1067 }
1068}
mtklein9db912c2015-05-19 11:11:26 -07001069
1070DEF_TEST(MiniRecorderLeftHanging, r) {
1071 // Any shader or other ref-counted effect will do just fine here.
1072 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -07001073 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
mtklein9db912c2015-05-19 11:11:26 -07001074
1075 SkMiniRecorder rec;
1076 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1077 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1078}
fmalita2ecc0002015-07-14 13:12:25 -07001079
1080DEF_TEST(Picture_preserveCullRect, r) {
1081 SkPictureRecorder recorder;
1082
1083 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1084 c->clear(SK_ColorCYAN);
1085
reedca2622b2016-03-18 07:25:55 -07001086 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -07001087 SkDynamicMemoryWStream wstream;
1088 picture->serialize(&wstream);
1089
Ben Wagner145dbcd2016-11-03 14:40:50 -04001090 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
1091 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
fmalita2ecc0002015-07-14 13:12:25 -07001092
mtklein5f939ab2016-03-16 10:28:35 -07001093 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -07001094 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1095 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1096 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1097 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1098}
fmalita796e3652016-05-13 11:40:07 -07001099
1100#if SK_SUPPORT_GPU
1101
1102DEF_TEST(PictureGpuAnalyzer, r) {
1103 SkPictureRecorder recorder;
1104
1105 {
1106 SkCanvas* canvas = recorder.beginRecording(10, 10);
1107 SkPaint paint;
1108 SkScalar intervals [] = { 10, 20 };
1109 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1110
1111 for (int i = 0; i < 50; ++i) {
1112 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1113 }
1114 }
1115 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1116
1117 SkPictureGpuAnalyzer analyzer;
1118 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1119
fmalita019db3f2016-05-31 06:32:57 -07001120 analyzer.analyzePicture(vetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001121 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1122
1123 analyzer.reset();
1124 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1125
1126 recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1127 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1128
fmalita019db3f2016-05-31 06:32:57 -07001129 analyzer.analyzePicture(nestedVetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001130 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalitab5fc58e2016-05-25 11:31:04 -07001131
1132 analyzer.reset();
1133
1134 const SkPath convexClip = make_convex_path();
1135 const SkPath concaveClip = make_concave_path();
1136 for (int i = 0; i < 50; ++i) {
Mike Reedc1f77742016-12-09 09:00:50 -05001137 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false);
1138 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true);
1139 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false);
fmalitab5fc58e2016-05-25 11:31:04 -07001140 }
1141 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1142
1143 for (int i = 0; i < 50; ++i) {
Mike Reedc1f77742016-12-09 09:00:50 -05001144 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true);
fmalitab5fc58e2016-05-25 11:31:04 -07001145 }
1146 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalita796e3652016-05-13 11:40:07 -07001147}
1148
1149#endif // SK_SUPPORT_GPU
reed41c27e12016-07-06 09:29:16 -07001150
1151///////////////////////////////////////////////////////////////////////////////////////////////////
1152
reedde996a02016-07-20 11:24:51 -07001153// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1154#if 0
reed41c27e12016-07-06 09:29:16 -07001155static void empty_ops(SkCanvas* canvas) {
1156}
1157static void clip_ops(SkCanvas* canvas) {
1158 canvas->save();
1159 canvas->clipRect(SkRect::MakeWH(20, 20));
1160 canvas->restore();
1161}
1162static void matrix_ops(SkCanvas* canvas) {
1163 canvas->save();
1164 canvas->scale(2, 3);
1165 canvas->restore();
1166}
1167static void matrixclip_ops(SkCanvas* canvas) {
1168 canvas->save();
1169 canvas->scale(2, 3);
1170 canvas->clipRect(SkRect::MakeWH(20, 20));
1171 canvas->restore();
1172}
1173typedef void (*CanvasProc)(SkCanvas*);
1174
1175// Test the kReturnNullForEmpty_FinishFlag option when recording
1176//
1177DEF_TEST(Picture_RecordEmpty, r) {
1178 const SkRect cull = SkRect::MakeWH(100, 100);
1179
1180 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1181
1182 for (auto proc : procs) {
1183 {
1184 SkPictureRecorder rec;
1185 proc(rec.beginRecording(cull));
1186 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1187 REPORTER_ASSERT(r, pic.get());
1188 REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1189 }
1190 {
1191 SkPictureRecorder rec;
1192 proc(rec.beginRecording(cull));
1193 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1194 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1195 REPORTER_ASSERT(r, !pic.get());
1196 }
1197 {
1198 SkPictureRecorder rec;
1199 proc(rec.beginRecording(cull));
1200 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1201 REPORTER_ASSERT(r, dr.get());
1202 }
1203 {
1204 SkPictureRecorder rec;
1205 proc(rec.beginRecording(cull));
1206 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1207 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1208 REPORTER_ASSERT(r, !dr.get());
1209 }
1210 }
1211}
reedde996a02016-07-20 11:24:51 -07001212#endif
1213