blob: 425d14f1933386c8582fa30c1aae9cb45414b23f [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"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000017#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000018#include "SkImageEncoder.h"
19#include "SkImageGenerator.h"
msarett8715d472016-02-17 10:02:29 -080020#include "SkMD5.h"
reed@google.com21b519d2012-10-02 17:42:15 +000021#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000022#include "SkPicture.h"
fmalita796e3652016-05-13 11:40:07 -070023#include "SkPictureAnalyzer.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000024#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000025#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070026#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080027#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070028#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000029#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000030#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080031#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000032#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000033#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070034#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000035
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000036#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000037
reed@google.com47b679b2014-05-14 18:58:16 +000038#include "SkLumaColorFilter.h"
39#include "SkColorFilterImageFilter.h"
40
reed@google.comfe7b1ed2012-11-29 21:00:39 +000041static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000042 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043 bm->eraseColor(color);
44 if (immutable) {
45 bm->setImmutable();
46 }
47}
48
mtkleina16af212015-08-26 08:14:52 -070049// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
50static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
reede3b38ce2016-01-08 09:18:44 -080051 // We just need _some_ SkImage
52 const SkPMColor pixel = 0;
53 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
reed9ce9d672016-03-17 10:51:11 -070054 sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel))));
mtkleina16af212015-08-26 08:14:52 -070055
56 SkPictureRecorder recorder;
reede3b38ce2016-01-08 09:18:44 -080057 recorder.beginRecording(100,100)->drawImage(image, 0,0);
reedca2622b2016-03-18 07:25:55 -070058 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtkleina16af212015-08-26 08:14:52 -070059
60 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
61}
62
tomhudson3a0f2792014-08-20 05:29:41 -070063/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070064static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070065 SkPictureRecorder recorder;
66
mtklein8e126562014-10-01 09:29:35 -070067 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070068 {
69 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
70 }
reedca2622b2016-03-18 07:25:55 -070071 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
tomhudson3a0f2792014-08-20 05:29:41 -070072 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
73
mtklein8e126562014-10-01 09:29:35 -070074 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070075 {
76 SkPaint paint;
77 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
78 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
79 SkBitmap bitmap;
80 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
81 bitmap.eraseColor(SK_ColorBLUE);
82 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
reed1a9b9642016-03-13 14:13:58 -070083 paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
84 SkShader::kClamp_TileMode));
85 REPORTER_ASSERT(reporter, paint.getShader()->isABitmap());
tomhudson3a0f2792014-08-20 05:29:41 -070086
87 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
88 }
reedca2622b2016-03-18 07:25:55 -070089 REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps());
tomhudson3a0f2792014-08-20 05:29:41 -070090}
91
92
scroggo@google.comd614c6a2012-09-14 17:26:37 +000093#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070094// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070095// in debug mode, so only run in debug mode.
96static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000097 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000098 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070099 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -0700100 // Turns that into an SkPicture
reedca2622b2016-03-18 07:25:55 -0700101 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillipsdb539902014-07-01 08:47:04 -0700102 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700103 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000104}
105
106// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
107static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000108 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700109 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700110 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000111 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000112 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000113}
114#endif
115
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000116static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000117 SkPaint paint;
118 SkRect rect = SkRect::MakeWH(50, 50);
119
120 SkScalar unit = rand.nextUScalar1();
121 if (unit <= 0.3) {
122// SkDebugf("save\n");
123 canvas->save();
124 } else if (unit <= 0.6) {
125// SkDebugf("restore\n");
126 canvas->restore();
127 } else if (unit <= 0.9) {
128// SkDebugf("clip\n");
129 canvas->clipRect(rect);
130 } else {
131// SkDebugf("draw\n");
132 canvas->drawPaint(paint);
133 }
134}
135
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000136#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700137
fmalitab5fc58e2016-05-25 11:31:04 -0700138static SkPath make_convex_path() {
139 SkPath path;
140 path.lineTo(100, 0);
141 path.lineTo(50, 100);
142 path.close();
143
144 return path;
145}
146
147static SkPath make_concave_path() {
148 SkPath path;
149 path.lineTo(50, 50);
150 path.lineTo(100, 0);
151 path.lineTo(50, 100);
152 path.close();
153
154 return path;
155}
156
mtklein8e126562014-10-01 09:29:35 -0700157static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000158 SkPictureRecorder recorder;
159
mtklein8e126562014-10-01 09:29:35 -0700160 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000161 {
162 SkPath path;
163 path.moveTo(0, 0);
164 path.lineTo(50, 50);
165
166 SkScalar intervals[] = { 1.0f, 1.0f };
reeda4393342016-03-18 11:22:57 -0700167 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000168
169 SkPaint paint;
170 paint.setStyle(SkPaint::kStroke_Style);
171 paint.setPathEffect(dash);
172
robertphillips98b03152015-01-26 11:29:36 -0800173 for (int i = 0; i < 50; ++i) {
174 canvas->drawPath(path, paint);
175 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000176 }
reedca2622b2016-03-18 07:25:55 -0700177 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000178 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000179
halcanary96fcdcc2015-08-27 07:41:13 -0700180 const char *reason = nullptr;
fmalita796e3652016-05-13 11:40:07 -0700181 REPORTER_ASSERT(reporter,
182 !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason));
bsalomon49f085d2014-09-05 13:34:00 -0700183 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000184
mtklein8e126562014-10-01 09:29:35 -0700185 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000186 {
187 SkPath path;
188
189 path.moveTo(0, 0);
190 path.lineTo(0, 50);
191 path.lineTo(25, 25);
192 path.lineTo(50, 50);
193 path.lineTo(50, 0);
194 path.close();
195 REPORTER_ASSERT(reporter, !path.isConvex());
196
197 SkPaint paint;
198 paint.setAntiAlias(true);
199 for (int i = 0; i < 50; ++i) {
200 canvas->drawPath(path, paint);
201 }
202 }
reedca2622b2016-03-18 07:25:55 -0700203 picture = recorder.finishRecordingAsPicture();
jvanverthd86b07a2014-11-04 08:50:15 -0800204 // A lot of small AA concave paths should be fine for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700205 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
jvanverthd86b07a2014-11-04 08:50:15 -0800206
207 canvas = recorder.beginRecording(100, 100);
208 {
209 SkPath path;
210
211 path.moveTo(0, 0);
212 path.lineTo(0, 100);
213 path.lineTo(50, 50);
214 path.lineTo(100, 100);
215 path.lineTo(100, 0);
216 path.close();
217 REPORTER_ASSERT(reporter, !path.isConvex());
218
219 SkPaint paint;
220 paint.setAntiAlias(true);
221 for (int i = 0; i < 50; ++i) {
222 canvas->drawPath(path, paint);
223 }
224 }
reedca2622b2016-03-18 07:25:55 -0700225 picture = recorder.finishRecordingAsPicture();
jvanverthd86b07a2014-11-04 08:50:15 -0800226 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700227 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000228
mtklein8e126562014-10-01 09:29:35 -0700229 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000230 {
231 SkPath path;
232
233 path.moveTo(0, 0);
234 path.lineTo(0, 50);
235 path.lineTo(25, 25);
236 path.lineTo(50, 50);
237 path.lineTo(50, 0);
238 path.close();
239 REPORTER_ASSERT(reporter, !path.isConvex());
240
241 SkPaint paint;
242 paint.setAntiAlias(true);
243 paint.setStyle(SkPaint::kStroke_Style);
244 paint.setStrokeWidth(0);
245 for (int i = 0; i < 50; ++i) {
246 canvas->drawPath(path, paint);
247 }
248 }
reedca2622b2016-03-18 07:25:55 -0700249 picture = recorder.finishRecordingAsPicture();
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000250 // hairline stroked AA concave paths are fine for GPU rendering
fmalita796e3652016-05-13 11:40:07 -0700251 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
tomhudson3a0f2792014-08-20 05:29:41 -0700252
mtklein8e126562014-10-01 09:29:35 -0700253 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700254 {
255 SkPaint paint;
256 SkScalar intervals [] = { 10, 20 };
reeda4393342016-03-18 11:22:57 -0700257 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
tomhudson3a0f2792014-08-20 05:29:41 -0700258
259 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800260
261 for (int i = 0; i < 50; ++i) {
262 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
263 }
tomhudson3a0f2792014-08-20 05:29:41 -0700264 }
reedca2622b2016-03-18 07:25:55 -0700265 picture = recorder.finishRecordingAsPicture();
tomhudson3a0f2792014-08-20 05:29:41 -0700266 // fast-path dashed effects are fine for GPU rendering ...
fmalita796e3652016-05-13 11:40:07 -0700267 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
tomhudson3a0f2792014-08-20 05:29:41 -0700268
mtklein8e126562014-10-01 09:29:35 -0700269 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700270 {
271 SkPaint paint;
272 SkScalar intervals [] = { 10, 20 };
reeda4393342016-03-18 11:22:57 -0700273 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
tomhudson3a0f2792014-08-20 05:29:41 -0700274
robertphillips98b03152015-01-26 11:29:36 -0800275 for (int i = 0; i < 50; ++i) {
276 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
277 }
tomhudson3a0f2792014-08-20 05:29:41 -0700278 }
reedca2622b2016-03-18 07:25:55 -0700279 picture = recorder.finishRecordingAsPicture();
tomhudson3a0f2792014-08-20 05:29:41 -0700280 // ... but only when applied to drawPoint() calls
fmalita796e3652016-05-13 11:40:07 -0700281 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
mtklein53fecfb2014-08-21 09:11:37 -0700282
fmalitab5fc58e2016-05-25 11:31:04 -0700283 canvas = recorder.beginRecording(100, 100);
284 {
285 const SkPath convexClip = make_convex_path();
286 const SkPath concaveClip = make_concave_path();
287
288 for (int i = 0; i < 50; ++i) {
289 canvas->clipPath(convexClip);
290 canvas->clipPath(concaveClip);
reed73603f32016-09-20 08:42:38 -0700291 canvas->clipPath(convexClip, SkCanvas::kIntersect_Op, true);
fmalitab5fc58e2016-05-25 11:31:04 -0700292 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
293 }
294 }
295 picture = recorder.finishRecordingAsPicture();
296 // Convex clips and non-AA concave clips are fine on the GPU.
297 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
298
299 canvas = recorder.beginRecording(100, 100);
300 {
301 const SkPath concaveClip = make_concave_path();
302 for (int i = 0; i < 50; ++i) {
reed73603f32016-09-20 08:42:38 -0700303 canvas->clipPath(concaveClip, SkCanvas::kIntersect_Op, true);
fmalitab5fc58e2016-05-25 11:31:04 -0700304 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
305 }
306 }
307 picture = recorder.finishRecordingAsPicture();
308 // ... but AA concave clips are not.
309 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
310
mtklein53fecfb2014-08-21 09:11:37 -0700311 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700312 canvas = recorder.beginRecording(100, 100);
313 {
fmalita796e3652016-05-13 11:40:07 -0700314 canvas->drawPicture(picture);
mtklein53fecfb2014-08-21 09:11:37 -0700315 }
reedca2622b2016-03-18 07:25:55 -0700316 picture = recorder.finishRecordingAsPicture();
fmalita796e3652016-05-13 11:40:07 -0700317 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000318}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000319
fmalita796e3652016-05-13 11:40:07 -0700320#endif // SK_SUPPORT_GPU
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000321
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000322static void set_canvas_to_save_count_4(SkCanvas* canvas) {
323 canvas->restoreToCount(1);
324 canvas->save();
325 canvas->save();
326 canvas->save();
327}
328
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000329/**
330 * A canvas that records the number of saves, saveLayers and restores.
331 */
332class SaveCountingCanvas : public SkCanvas {
333public:
334 SaveCountingCanvas(int width, int height)
335 : INHERITED(width, height)
336 , fSaveCount(0)
337 , fSaveLayerCount(0)
338 , fRestoreCount(0){
339 }
340
reed4960eee2015-12-18 07:09:18 -0800341 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000342 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800343 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000344 }
345
mtklein36352bf2015-03-25 18:17:31 -0700346 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000347 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400348 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000349 }
350
mtklein36352bf2015-03-25 18:17:31 -0700351 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000352 ++fRestoreCount;
353 this->INHERITED::willRestore();
354 }
355
356 unsigned int getSaveCount() const { return fSaveCount; }
357 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
358 unsigned int getRestoreCount() const { return fRestoreCount; }
359
360private:
361 unsigned int fSaveCount;
362 unsigned int fSaveLayerCount;
363 unsigned int fRestoreCount;
364
365 typedef SkCanvas INHERITED;
366};
367
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000368void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000369 unsigned int numSaves, unsigned int numSaveLayers,
370 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700371 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700372 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000373
robertphillipsc5ba71d2014-09-04 08:42:50 -0700374 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000375
mtklein87c41382014-09-08 07:31:18 -0700376 // Optimizations may have removed these,
377 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
378 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
379 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
380 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000381}
382
383// This class exists so SkPicture can friend it and give it access to
384// the 'partialReplay' method.
385class SkPictureRecorderReplayTester {
386public:
reedca2622b2016-03-18 07:25:55 -0700387 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000388 SkPictureRecorder recorder2;
389
robertphillips9f1c2412014-06-09 06:25:34 -0700390 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000391
392 recorder->partialReplay(canvas);
393
reedca2622b2016-03-18 07:25:55 -0700394 return recorder2.finishRecordingAsPicture();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000395 }
396};
397
robertphillips9058d602014-06-10 11:45:46 -0700398static void create_imbalance(SkCanvas* canvas) {
399 SkRect clipRect = SkRect::MakeWH(2, 2);
400 SkRect drawRect = SkRect::MakeWH(10, 10);
401 canvas->save();
reed73603f32016-09-20 08:42:38 -0700402 canvas->clipRect(clipRect, SkCanvas::kReplace_Op);
robertphillips9058d602014-06-10 11:45:46 -0700403 canvas->translate(1.0f, 1.0f);
404 SkPaint p;
405 p.setColor(SK_ColorGREEN);
406 canvas->drawRect(drawRect, p);
407 // no restore
408}
409
410// This tests that replaying a potentially unbalanced picture into a canvas
411// doesn't affect the canvas' save count or matrix/clip state.
412static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
413 SkBitmap bm;
414 bm.allocN32Pixels(4, 3);
415 SkCanvas canvas(bm);
416
417 int beforeSaveCount = canvas.getSaveCount();
418
419 SkMatrix beforeMatrix = canvas.getTotalMatrix();
420
421 SkRect beforeClip;
422
423 canvas.getClipBounds(&beforeClip);
424
425 canvas.drawPicture(picture);
426
427 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
428 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
429
430 SkRect afterClip;
431
432 canvas.getClipBounds(&afterClip);
433
434 REPORTER_ASSERT(reporter, afterClip == beforeClip);
435}
436
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000437// Test out SkPictureRecorder::partialReplay
438DEF_TEST(PictureRecorder_replay, reporter) {
439 // check save/saveLayer state
440 {
441 SkPictureRecorder recorder;
442
robertphillips9f1c2412014-06-09 06:25:34 -0700443 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000444
halcanary96fcdcc2015-08-27 07:41:13 -0700445 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000446
reedca2622b2016-03-18 07:25:55 -0700447 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000448
449 // The extra save and restore comes from the Copy process.
reedca2622b2016-03-18 07:25:55 -0700450 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000451
halcanary96fcdcc2015-08-27 07:41:13 -0700452 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000453
reedca2622b2016-03-18 07:25:55 -0700454 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000455
reedca2622b2016-03-18 07:25:55 -0700456 check_save_state(reporter, final.get(), 1, 2, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000457
458 // The copy shouldn't pick up any operations added after it was made
reedca2622b2016-03-18 07:25:55 -0700459 check_save_state(reporter, copy.get(), 2, 1, 3);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000460 }
461
462 // (partially) check leakage of draw ops
463 {
464 SkPictureRecorder recorder;
465
robertphillips9f1c2412014-06-09 06:25:34 -0700466 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000467
468 SkRect r = SkRect::MakeWH(5, 5);
469 SkPaint p;
470
471 canvas->drawRect(r, p);
472
reedca2622b2016-03-18 07:25:55 -0700473 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000474
475 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
476
477 SkBitmap bm;
478 make_bm(&bm, 10, 10, SK_ColorRED, true);
479
480 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700481 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000482
reedca2622b2016-03-18 07:25:55 -0700483 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000484 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
485
486 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
487
488 // The snapshot shouldn't pick up any operations added after it was made
489 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
490 }
robertphillips9058d602014-06-10 11:45:46 -0700491
492 // Recreate the Android partialReplay test case
493 {
494 SkPictureRecorder recorder;
495
halcanary96fcdcc2015-08-27 07:41:13 -0700496 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700497 create_imbalance(canvas);
498
499 int expectedSaveCount = canvas->getSaveCount();
500
reedca2622b2016-03-18 07:25:55 -0700501 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
502 check_balance(reporter, copy.get());
robertphillips9058d602014-06-10 11:45:46 -0700503
504 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
505
506 // End the recording of source to test the picture finalization
507 // process isn't complicated by the partialReplay step
reedca2622b2016-03-18 07:25:55 -0700508 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
robertphillips9058d602014-06-10 11:45:46 -0700509 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000510}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000511
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000512static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
513 SkCanvas testCanvas(100, 100);
514 set_canvas_to_save_count_4(&testCanvas);
515
516 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
517
518 SkPaint paint;
519 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
520
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000521 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000522
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000523 {
524 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700525 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000526 canvas->save();
527 canvas->translate(10, 10);
528 canvas->drawRect(rect, paint);
529 canvas->save();
530 canvas->translate(10, 10);
531 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700532 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000533
robertphillips9b14f262014-06-04 05:40:44 -0700534 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000535 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
536 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000537
538 set_canvas_to_save_count_4(&testCanvas);
539
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000540 {
541 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700542 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000543 canvas->save();
544 canvas->translate(10, 10);
545 canvas->drawRect(rect, paint);
546 canvas->save();
547 canvas->translate(10, 10);
548 canvas->drawRect(rect, paint);
549 canvas->restore();
550 canvas->restore();
551 canvas->restore();
552 canvas->restore();
reedca2622b2016-03-18 07:25:55 -0700553 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000554
robertphillips9b14f262014-06-04 05:40:44 -0700555 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000556 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
557 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000558
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000559 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000560
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000561 {
robertphillips9f1c2412014-06-09 06:25:34 -0700562 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000563 canvas->translate(10, 10);
564 canvas->drawRect(rect, paint);
reedca2622b2016-03-18 07:25:55 -0700565 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000566
robertphillips9b14f262014-06-04 05:40:44 -0700567 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000568 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
569 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
570 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000571}
572
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000573static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000574 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000575
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000576 SkPictureRecorder recorder;
577
reed@google.com21b519d2012-10-02 17:42:15 +0000578 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000579 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000580
robertphillips9f1c2412014-06-09 06:25:34 -0700581 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000582
583 for (int i = 0; i < 1000; ++i) {
584 rand_op(canvas, rand);
585 }
reedca2622b2016-03-18 07:25:55 -0700586 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000587
588 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000589 }
590
591 {
robertphillips9f1c2412014-06-09 06:25:34 -0700592 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000593 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000594
reed@google.com21b519d2012-10-02 17:42:15 +0000595 for (int i = 0; i < 100; ++i) {
596 canvas->save();
597 }
598 while (canvas->getSaveCount() > 1) {
599 canvas->clipRect(rect);
600 canvas->restore();
601 }
reedca2622b2016-03-18 07:25:55 -0700602 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
reed@google.com21b519d2012-10-02 17:42:15 +0000603 }
604}
605
scroggo@google.com4b90b112012-12-04 15:08:56 +0000606#ifndef SK_DEBUG
607// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
608// should never do this.
609static void test_bad_bitmap() {
610 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
611 // fail.
612 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000613 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000614 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700615 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000616 recordingCanvas->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700617 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000618
619 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700620 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000621}
622#endif
623
bungeman38d909e2016-08-02 14:40:46 -0700624static sk_sp<SkData> serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000625 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700626 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700627 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000628 canvas->drawBitmap(bitmap, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700629 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000630
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000631 SkDynamicMemoryWStream wStream;
halcanaryf2848b62015-12-10 12:40:23 -0800632 SkAutoTUnref<SkPixelSerializer> serializer(
633 SkImageEncoder::CreatePixelSerializer());
634 picture->serialize(&wStream, serializer);
reed42943c82016-09-12 12:01:44 -0700635 return wStream.detachAsData();
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000636}
637
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000638struct ErrorContext {
639 int fErrors;
640 skiatest::Reporter* fReporter;
641};
642
643static void assert_one_parse_error_cb(SkError error, void* context) {
644 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
645 errorContext->fErrors++;
646 // This test only expects one error, and that is a kParseError. If there are others,
647 // there is some unknown problem.
648 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
649 "This threw more errors than expected.");
650 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
651 SkGetLastErrorString());
652}
653
msarett8715d472016-02-17 10:02:29 -0800654static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
655 SkAutoLockPixels autoLockPixels(bm);
656 SkASSERT(bm.getPixels());
657 SkMD5 md5;
658 size_t rowLen = bm.info().bytesPerPixel() * bm.width();
659 for (int y = 0; y < bm.height(); ++y) {
halcanary1e903042016-04-25 10:29:36 -0700660 md5.write(bm.getAddr(0, y), rowLen);
msarett8715d472016-02-17 10:02:29 -0800661 }
662 md5.finish(*digest);
663}
664
665DEF_TEST(Picture_EncodedData, reporter) {
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000666 // Create a bitmap that will be encoded.
667 SkBitmap original;
668 make_bm(&original, 100, 100, SK_ColorBLUE, true);
669 SkDynamicMemoryWStream wStream;
670 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
671 return;
672 }
reed42943c82016-09-12 12:01:44 -0700673 sk_sp<SkData> data = wStream.detachAsData();
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000674
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000675 SkBitmap bm;
bungeman38d909e2016-08-02 14:40:46 -0700676 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data.get(), &bm);
reed@google.combf790232013-12-13 19:45:58 +0000677 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000678
679 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
680 // Flattening original will follow the old path of performing an encode, while flattening bm
681 // will use the already encoded data.
bungeman38d909e2016-08-02 14:40:46 -0700682 sk_sp<SkData> picture1(serialized_picture_from_bitmap(original));
683 sk_sp<SkData> picture2(serialized_picture_from_bitmap(bm));
684 REPORTER_ASSERT(reporter, picture1->equals(picture2.get()));
msarett8715d472016-02-17 10:02:29 -0800685
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000686 // Now test that a parse error was generated when trying to create a new SkPicture without
687 // providing a function to decode the bitmap.
688 ErrorContext context;
689 context.fErrors = 0;
690 context.fReporter = reporter;
691 SkSetErrorCallback(assert_one_parse_error_cb, &context);
bungeman38d909e2016-08-02 14:40:46 -0700692 SkMemoryStream pictureStream(std::move(picture1));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000693 SkClearLastError();
reeda9ca05c2016-08-11 03:55:15 -0700694 sk_sp<SkPicture> pictureFromStream(SkPicture::MakeFromStream(&pictureStream));
halcanary96fcdcc2015-08-27 07:41:13 -0700695 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000696 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700697 SkSetErrorCallback(nullptr, nullptr);
msarett8715d472016-02-17 10:02:29 -0800698
699 // Test that using the version of CreateFromStream that just takes a stream also decodes the
700 // bitmap. Drawing this picture should look exactly like the original bitmap.
701 SkMD5::Digest referenceDigest;
702 md5(original, &referenceDigest);
703
704 SkBitmap dst;
705 dst.allocPixels(original.info());
706 dst.eraseColor(SK_ColorRED);
707 SkCanvas canvas(dst);
708
709 pictureStream.rewind();
reedca2622b2016-03-18 07:25:55 -0700710 pictureFromStream = SkPicture::MakeFromStream(&pictureStream);
msarett8715d472016-02-17 10:02:29 -0800711 canvas.drawPicture(pictureFromStream.get());
712
713 SkMD5::Digest digest2;
714 md5(dst, &digest2);
715 REPORTER_ASSERT(reporter, referenceDigest == digest2);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000716}
717
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000718static void test_clip_bound_opt(skiatest::Reporter* reporter) {
719 // Test for crbug.com/229011
720 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
721 SkIntToScalar(2), SkIntToScalar(2));
722 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
723 SkIntToScalar(1), SkIntToScalar(1));
724 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
725 SkIntToScalar(1), SkIntToScalar(1));
726
727 SkPath invPath;
728 invPath.addOval(rect1);
729 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
730 SkPath path;
731 path.addOval(rect2);
732 SkPath path2;
733 path2.addOval(rect3);
734 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000735 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700736
737 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000738 {
robertphillips9f1c2412014-06-09 06:25:34 -0700739 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700740 canvas->clipPath(invPath);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000741 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
742 REPORTER_ASSERT(reporter, true == nonEmpty);
743 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
744 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
745 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
746 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
747 }
748 {
robertphillips9f1c2412014-06-09 06:25:34 -0700749 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700750 canvas->clipPath(path);
751 canvas->clipPath(invPath);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000752 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
753 REPORTER_ASSERT(reporter, true == nonEmpty);
754 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
755 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
756 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
757 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
758 }
759 {
robertphillips9f1c2412014-06-09 06:25:34 -0700760 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700761 canvas->clipPath(path);
762 canvas->clipPath(invPath, SkCanvas::kUnion_Op);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000763 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
764 REPORTER_ASSERT(reporter, true == nonEmpty);
765 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
766 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
767 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
768 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
769 }
770 {
robertphillips9f1c2412014-06-09 06:25:34 -0700771 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700772 canvas->clipPath(path, SkCanvas::kDifference_Op);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000773 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
774 REPORTER_ASSERT(reporter, true == nonEmpty);
775 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
776 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
777 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
778 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
779 }
780 {
robertphillips9f1c2412014-06-09 06:25:34 -0700781 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700782 canvas->clipPath(path, SkCanvas::kReverseDifference_Op);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000783 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
784 // True clip is actually empty in this case, but the best
785 // determination we can make using only bounds as input is that the
786 // clip is included in the bounds of 'path'.
787 REPORTER_ASSERT(reporter, true == nonEmpty);
788 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
789 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
790 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
791 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
792 }
793 {
robertphillips9f1c2412014-06-09 06:25:34 -0700794 SkCanvas* canvas = recorder.beginRecording(10, 10);
reed73603f32016-09-20 08:42:38 -0700795 canvas->clipPath(path, SkCanvas::kIntersect_Op);
796 canvas->clipPath(path2, SkCanvas::kXOR_Op);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000797 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
798 REPORTER_ASSERT(reporter, true == nonEmpty);
799 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
800 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
801 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
802 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
803 }
804}
805
schenneyeeff8bb2015-07-07 14:27:10 -0700806static void test_cull_rect_reset(skiatest::Reporter* reporter) {
807 SkPictureRecorder recorder;
808 SkRect bounds = SkRect::MakeWH(10, 10);
809 SkRTreeFactory factory;
810 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
811 bounds = SkRect::MakeWH(100, 100);
812 SkPaint paint;
813 canvas->drawRect(bounds, paint);
814 canvas->drawRect(bounds, paint);
reedca2622b2016-03-18 07:25:55 -0700815 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
mtkleineedc3342015-07-08 08:26:39 -0700816 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -0700817 REPORTER_ASSERT(reporter, picture);
818
819 SkRect finalCullRect = picture->cullRect();
820 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
821 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
822 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
823 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
824
825 const SkBBoxHierarchy* pictureBBH = picture->bbh();
826 SkRect bbhCullRect = pictureBBH->getRootBound();
827 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
828 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
829 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
830 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
831}
832
833
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000834/**
835 * A canvas that records the number of clip commands.
836 */
837class ClipCountingCanvas : public SkCanvas {
838public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000839 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000840 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000841 , fClipCount(0){
842 }
843
reed73603f32016-09-20 08:42:38 -0700844 void onClipRect(const SkRect& r, ClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000845 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000846 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000847 }
848
reed73603f32016-09-20 08:42:38 -0700849 void onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000850 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000851 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000852 }
853
reed73603f32016-09-20 08:42:38 -0700854 void onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000855 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000856 this->INHERITED::onClipPath(path, op, edgeStyle);
857 }
858
reed73603f32016-09-20 08:42:38 -0700859 void onClipRegion(const SkRegion& deviceRgn, ClipOp op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000860 fClipCount += 1;
861 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000862 }
863
864 unsigned getClipCount() const { return fClipCount; }
865
866private:
867 unsigned fClipCount;
868
869 typedef SkCanvas INHERITED;
870};
871
872static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000873 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700874 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000875
reed73603f32016-09-20 08:42:38 -0700876 canvas->clipRect(SkRect::MakeEmpty(), SkCanvas::kReplace_Op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000877 // The following expanding clip should not be skipped.
reed73603f32016-09-20 08:42:38 -0700878 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkCanvas::kUnion_Op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000879 // Draw something so the optimizer doesn't just fold the world.
880 SkPaint p;
881 p.setColor(SK_ColorBLUE);
882 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700883 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000884
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000885 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700886 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000887
888 // Both clips should be present on playback.
889 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
890}
891
tomhudson@google.com381010e2013-10-24 11:12:47 +0000892static void test_hierarchical(skiatest::Reporter* reporter) {
893 SkBitmap bm;
894 make_bm(&bm, 10, 10, SK_ColorRED, true);
895
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000896 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000897
robertphillips9f1c2412014-06-09 06:25:34 -0700898 recorder.beginRecording(10, 10);
reedca2622b2016-03-18 07:25:55 -0700899 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000900 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +0000901
robertphillips9f1c2412014-06-09 06:25:34 -0700902 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700903 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000904 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +0000905
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000906 {
robertphillips9f1c2412014-06-09 06:25:34 -0700907 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700908 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700909 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000910 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
911 }
912 {
robertphillips9f1c2412014-06-09 06:25:34 -0700913 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700914 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700915 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000916 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
917 }
918 {
robertphillips9f1c2412014-06-09 06:25:34 -0700919 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000920 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700921 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700922 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000923 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
924 }
925 {
robertphillips9f1c2412014-06-09 06:25:34 -0700926 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000927 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700928 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700929 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000930 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
931 }
tomhudson@google.com381010e2013-10-24 11:12:47 +0000932}
933
robertphillips@google.comd5500882014-04-02 23:51:13 +0000934static void test_gen_id(skiatest::Reporter* reporter) {
935
Robert Phillipscfaeec42014-07-13 12:00:50 -0400936 SkPictureRecorder recorder;
937 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700938 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000939
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000940 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400941 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000942
robertphillips9f1c2412014-06-09 06:25:34 -0700943 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000944 canvas->drawARGB(255, 255, 255, 255);
reedca2622b2016-03-18 07:25:55 -0700945 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000946 // picture should have a non-zero id after recording
947 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000948
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000949 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400950 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000951}
952
caryclark5ef194c2015-08-31 09:22:38 -0700953static void test_typeface(skiatest::Reporter* reporter) {
954 SkPictureRecorder recorder;
955 SkCanvas* canvas = recorder.beginRecording(10, 10);
956 SkPaint paint;
mbocee6a9912016-05-31 11:42:36 -0700957 paint.setTypeface(SkTypeface::MakeFromName("Arial",
958 SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
caryclark5ef194c2015-08-31 09:22:38 -0700959 canvas->drawText("Q", 1, 0, 10, paint);
reedca2622b2016-03-18 07:25:55 -0700960 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700961 SkDynamicMemoryWStream stream;
962 picture->serialize(&stream);
963}
964
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000965DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700966 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000967#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700968 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000969 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000970#else
971 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000972#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000973 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000974 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000975#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -0700976 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000977#endif
mtkleina16af212015-08-26 08:14:52 -0700978 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -0700979 test_analysis(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000980 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000981 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000982 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000983 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700984 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000985}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000986
987static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
988 const SkPaint paint;
989 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
990 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700991 int divs[] = { 2, 3 };
992 SkCanvas::Lattice lattice;
993 lattice.fXCount = lattice.fYCount = 2;
994 lattice.fXDivs = lattice.fYDivs = divs;
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000995
996 // Don't care what these record, as long as they're legal.
997 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -0700998 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000999 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -08001000 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -07001001 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001002}
1003
1004static void test_draw_bitmaps(SkCanvas* canvas) {
1005 SkBitmap empty;
1006 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001007 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001008 draw_bitmaps(empty, canvas);
1009}
1010
1011DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001012 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001013 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -07001014 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001015}
1016
1017DEF_TEST(Canvas_EmptyBitmap, r) {
1018 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001019 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001020 SkCanvas canvas(dst);
1021
1022 test_draw_bitmaps(&canvas);
1023}
dneto3f22e8c2014-07-30 15:42:22 -07001024
1025DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1026 // This test is from crbug.com/344987.
1027 // The commands are:
1028 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001029 // drawBitmapRect
1030 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001031 // restore
1032 // The bug was that this structure was modified so that:
1033 // - The saveLayer and restore were eliminated
1034 // - The alpha was only applied to the first drawBitmapRectToRect
1035
1036 // This test draws blue and red squares inside a 50% transparent
1037 // layer. Both colours should show up muted.
1038 // When the bug is present, the red square (the second bitmap)
1039 // shows upwith full opacity.
1040
1041 SkBitmap blueBM;
1042 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1043 SkBitmap redBM;
1044 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1045 SkPaint semiTransparent;
1046 semiTransparent.setAlpha(0x80);
1047
1048 SkPictureRecorder recorder;
1049 SkCanvas* canvas = recorder.beginRecording(100, 100);
1050 canvas->drawARGB(0, 0, 0, 0);
1051
1052 canvas->saveLayer(0, &semiTransparent);
1053 canvas->drawBitmap(blueBM, 25, 25);
1054 canvas->drawBitmap(redBM, 50, 50);
1055 canvas->restore();
1056
reedca2622b2016-03-18 07:25:55 -07001057 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -07001058
1059 // Now replay the picture back on another canvas
1060 // and check a couple of its pixels.
1061 SkBitmap replayBM;
1062 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1063 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001064 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001065 replayCanvas.flush();
1066
1067 // With the bug present, at (55, 55) we would get a fully opaque red
1068 // intead of a dark red.
1069 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1070 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1071}
mtklein3e8232b2014-08-18 13:39:11 -07001072
1073struct CountingBBH : public SkBBoxHierarchy {
1074 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001075 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001076
schenney23d85932015-03-06 16:20:28 -08001077 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001078
mtkleinc6ad06a2015-08-19 09:51:00 -07001079 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001080 this->searchCalls++;
1081 }
1082
mtklein36352bf2015-03-25 18:17:31 -07001083 void insert(const SkRect[], int) override {}
1084 virtual size_t bytesUsed() const override { return 0; }
1085 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001086};
1087
1088class SpoonFedBBHFactory : public SkBBHFactory {
1089public:
1090 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001091 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001092 return SkRef(fBBH);
1093 }
1094private:
1095 SkBBoxHierarchy* fBBH;
1096};
1097
1098// When the canvas clip covers the full picture, we don't need to call the BBH.
1099DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001100 SkRect bound = SkRect::MakeWH(320, 240);
1101 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001102 SpoonFedBBHFactory factory(&bbh);
1103
1104 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001105 SkCanvas* c = recorder.beginRecording(bound, &factory);
1106 // Record a few ops so we don't hit a small- or empty- picture optimization.
1107 c->drawRect(bound, SkPaint());
1108 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -07001109 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -07001110
1111 SkCanvas big(640, 480), small(300, 200);
1112
robertphillipsc5ba71d2014-09-04 08:42:50 -07001113 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001114 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1115
robertphillipsc5ba71d2014-09-04 08:42:50 -07001116 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001117 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1118}
mtkleind72094d2014-08-27 12:12:23 -07001119
1120DEF_TEST(Picture_BitmapLeak, r) {
1121 SkBitmap mut, immut;
1122 mut.allocN32Pixels(300, 200);
1123 immut.allocN32Pixels(300, 200);
1124 immut.setImmutable();
1125 SkASSERT(!mut.isImmutable());
1126 SkASSERT(immut.isImmutable());
1127
1128 // No one can hold a ref on our pixels yet.
1129 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1130 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1131
reedca2622b2016-03-18 07:25:55 -07001132 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -08001133 {
1134 // we want the recorder to go out of scope before our subsequent checks, so we
1135 // place it inside local braces.
1136 SkPictureRecorder rec;
1137 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1138 canvas->drawBitmap(mut, 0, 0);
1139 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -07001140 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -08001141 }
mtkleind72094d2014-08-27 12:12:23 -07001142
1143 // The picture shares the immutable pixels but copies the mutable ones.
1144 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1145 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1146
1147 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -07001148 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -07001149 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1150 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1151}
mtkleinfeaadee2015-04-08 11:25:48 -07001152
1153// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1154DEF_TEST(Picture_getRecordingCanvas, r) {
1155 SkPictureRecorder rec;
1156 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1157 for (int i = 0; i < 3; i++) {
1158 rec.beginRecording(100, 100);
1159 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -07001160 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -07001161 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1162 }
1163}
mtklein9db912c2015-05-19 11:11:26 -07001164
1165DEF_TEST(MiniRecorderLeftHanging, r) {
1166 // Any shader or other ref-counted effect will do just fine here.
1167 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -07001168 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
mtklein9db912c2015-05-19 11:11:26 -07001169
1170 SkMiniRecorder rec;
1171 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1172 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1173}
fmalita2ecc0002015-07-14 13:12:25 -07001174
1175DEF_TEST(Picture_preserveCullRect, r) {
1176 SkPictureRecorder recorder;
1177
1178 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1179 c->clear(SK_ColorCYAN);
1180
reedca2622b2016-03-18 07:25:55 -07001181 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -07001182 SkDynamicMemoryWStream wstream;
1183 picture->serialize(&wstream);
1184
1185 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
reedca2622b2016-03-18 07:25:55 -07001186 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream));
fmalita2ecc0002015-07-14 13:12:25 -07001187
mtklein5f939ab2016-03-16 10:28:35 -07001188 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -07001189 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1190 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1191 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1192 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1193}
fmalita796e3652016-05-13 11:40:07 -07001194
1195#if SK_SUPPORT_GPU
1196
1197DEF_TEST(PictureGpuAnalyzer, r) {
1198 SkPictureRecorder recorder;
1199
1200 {
1201 SkCanvas* canvas = recorder.beginRecording(10, 10);
1202 SkPaint paint;
1203 SkScalar intervals [] = { 10, 20 };
1204 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1205
1206 for (int i = 0; i < 50; ++i) {
1207 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1208 }
1209 }
1210 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1211
1212 SkPictureGpuAnalyzer analyzer;
1213 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1214
fmalita019db3f2016-05-31 06:32:57 -07001215 analyzer.analyzePicture(vetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001216 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1217
1218 analyzer.reset();
1219 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1220
1221 recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1222 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1223
fmalita019db3f2016-05-31 06:32:57 -07001224 analyzer.analyzePicture(nestedVetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001225 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalitab5fc58e2016-05-25 11:31:04 -07001226
1227 analyzer.reset();
1228
1229 const SkPath convexClip = make_convex_path();
1230 const SkPath concaveClip = make_concave_path();
1231 for (int i = 0; i < 50; ++i) {
reed73603f32016-09-20 08:42:38 -07001232 analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, false);
1233 analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, true);
1234 analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, false);
fmalitab5fc58e2016-05-25 11:31:04 -07001235 }
1236 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1237
1238 for (int i = 0; i < 50; ++i) {
reed73603f32016-09-20 08:42:38 -07001239 analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, true);
fmalitab5fc58e2016-05-25 11:31:04 -07001240 }
1241 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalita796e3652016-05-13 11:40:07 -07001242}
1243
1244#endif // SK_SUPPORT_GPU
reed41c27e12016-07-06 09:29:16 -07001245
1246///////////////////////////////////////////////////////////////////////////////////////////////////
1247
reedde996a02016-07-20 11:24:51 -07001248// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1249#if 0
reed41c27e12016-07-06 09:29:16 -07001250static void empty_ops(SkCanvas* canvas) {
1251}
1252static void clip_ops(SkCanvas* canvas) {
1253 canvas->save();
1254 canvas->clipRect(SkRect::MakeWH(20, 20));
1255 canvas->restore();
1256}
1257static void matrix_ops(SkCanvas* canvas) {
1258 canvas->save();
1259 canvas->scale(2, 3);
1260 canvas->restore();
1261}
1262static void matrixclip_ops(SkCanvas* canvas) {
1263 canvas->save();
1264 canvas->scale(2, 3);
1265 canvas->clipRect(SkRect::MakeWH(20, 20));
1266 canvas->restore();
1267}
1268typedef void (*CanvasProc)(SkCanvas*);
1269
1270// Test the kReturnNullForEmpty_FinishFlag option when recording
1271//
1272DEF_TEST(Picture_RecordEmpty, r) {
1273 const SkRect cull = SkRect::MakeWH(100, 100);
1274
1275 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1276
1277 for (auto proc : procs) {
1278 {
1279 SkPictureRecorder rec;
1280 proc(rec.beginRecording(cull));
1281 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1282 REPORTER_ASSERT(r, pic.get());
1283 REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1284 }
1285 {
1286 SkPictureRecorder rec;
1287 proc(rec.beginRecording(cull));
1288 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1289 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1290 REPORTER_ASSERT(r, !pic.get());
1291 }
1292 {
1293 SkPictureRecorder rec;
1294 proc(rec.beginRecording(cull));
1295 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1296 REPORTER_ASSERT(r, dr.get());
1297 }
1298 {
1299 SkPictureRecorder rec;
1300 proc(rec.beginRecording(cull));
1301 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1302 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1303 REPORTER_ASSERT(r, !dr.get());
1304 }
1305 }
1306}
reedde996a02016-07-20 11:24:51 -07001307#endif
1308