blob: 6ab57c04d741a207fb92f6fe9ab3ba7522c3b8ca [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);
291 canvas->clipPath(convexClip, SkRegion::kIntersect_Op, true);
292 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) {
303 canvas->clipPath(concaveClip, SkRegion::kIntersect_Op, true);
304 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();
402 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
403 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000740 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
741 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000750 canvas->clipPath(path, SkRegion::kIntersect_Op);
751 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
752 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000761 canvas->clipPath(path, SkRegion::kIntersect_Op);
762 canvas->clipPath(invPath, SkRegion::kUnion_Op);
763 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000772 canvas->clipPath(path, SkRegion::kDifference_Op);
773 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000782 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
783 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);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000795 canvas->clipPath(path, SkRegion::kIntersect_Op);
796 canvas->clipPath(path2, SkRegion::kXOR_Op);
797 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
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000844 virtual void onClipRect(const SkRect& r,
845 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -0700846 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000847 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000848 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000849 }
850
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000851 virtual void onClipRRect(const SkRRect& rrect,
852 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -0700853 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000854 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000855 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000856 }
857
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000858 virtual void onClipPath(const SkPath& path,
859 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -0700860 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000861 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000862 this->INHERITED::onClipPath(path, op, edgeStyle);
863 }
864
mtklein36352bf2015-03-25 18:17:31 -0700865 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000866 fClipCount += 1;
867 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000868 }
869
870 unsigned getClipCount() const { return fClipCount; }
871
872private:
873 unsigned fClipCount;
874
875 typedef SkCanvas INHERITED;
876};
877
878static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000879 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700880 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000881
882 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
883 // The following expanding clip should not be skipped.
884 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
885 // Draw something so the optimizer doesn't just fold the world.
886 SkPaint p;
887 p.setColor(SK_ColorBLUE);
888 canvas->drawPaint(p);
reedca2622b2016-03-18 07:25:55 -0700889 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000890
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000891 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -0700892 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000893
894 // Both clips should be present on playback.
895 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
896}
897
tomhudson@google.com381010e2013-10-24 11:12:47 +0000898static void test_hierarchical(skiatest::Reporter* reporter) {
899 SkBitmap bm;
900 make_bm(&bm, 10, 10, SK_ColorRED, true);
901
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000902 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000903
robertphillips9f1c2412014-06-09 06:25:34 -0700904 recorder.beginRecording(10, 10);
reedca2622b2016-03-18 07:25:55 -0700905 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000906 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +0000907
robertphillips9f1c2412014-06-09 06:25:34 -0700908 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
reedca2622b2016-03-18 07:25:55 -0700909 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000910 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +0000911
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000912 {
robertphillips9f1c2412014-06-09 06:25:34 -0700913 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700914 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700915 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000916 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
917 }
918 {
robertphillips9f1c2412014-06-09 06:25:34 -0700919 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -0700920 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700921 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000922 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
923 }
924 {
robertphillips9f1c2412014-06-09 06:25:34 -0700925 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000926 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700927 canvas->drawPicture(childPlain);
reedca2622b2016-03-18 07:25:55 -0700928 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000929 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
930 }
931 {
robertphillips9f1c2412014-06-09 06:25:34 -0700932 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000933 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -0700934 canvas->drawPicture(childWithBitmap);
reedca2622b2016-03-18 07:25:55 -0700935 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000936 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
937 }
tomhudson@google.com381010e2013-10-24 11:12:47 +0000938}
939
robertphillips@google.comd5500882014-04-02 23:51:13 +0000940static void test_gen_id(skiatest::Reporter* reporter) {
941
Robert Phillipscfaeec42014-07-13 12:00:50 -0400942 SkPictureRecorder recorder;
943 recorder.beginRecording(0, 0);
reedca2622b2016-03-18 07:25:55 -0700944 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000945
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000946 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -0400947 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000948
robertphillips9f1c2412014-06-09 06:25:34 -0700949 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000950 canvas->drawARGB(255, 255, 255, 255);
reedca2622b2016-03-18 07:25:55 -0700951 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000952 // picture should have a non-zero id after recording
953 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000954
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000955 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -0400956 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +0000957}
958
caryclark5ef194c2015-08-31 09:22:38 -0700959static void test_typeface(skiatest::Reporter* reporter) {
960 SkPictureRecorder recorder;
961 SkCanvas* canvas = recorder.beginRecording(10, 10);
962 SkPaint paint;
mbocee6a9912016-05-31 11:42:36 -0700963 paint.setTypeface(SkTypeface::MakeFromName("Arial",
964 SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
caryclark5ef194c2015-08-31 09:22:38 -0700965 canvas->drawText("Q", 1, 0, 10, paint);
reedca2622b2016-03-18 07:25:55 -0700966 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
caryclark5ef194c2015-08-31 09:22:38 -0700967 SkDynamicMemoryWStream stream;
968 picture->serialize(&stream);
969}
970
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000971DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -0700972 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000973#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -0700974 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000975 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +0000976#else
977 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000978#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000979 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000980 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000981#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -0700982 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000983#endif
mtkleina16af212015-08-26 08:14:52 -0700984 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -0700985 test_analysis(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000986 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000987 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +0000988 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000989 test_gen_id(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -0700990 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000991}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +0000992
993static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
994 const SkPaint paint;
995 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
996 const SkIRect irect = { 2, 2, 3, 3 };
msarettc573a402016-08-02 08:05:56 -0700997 int divs[] = { 2, 3 };
998 SkCanvas::Lattice lattice;
999 lattice.fXCount = lattice.fYCount = 2;
1000 lattice.fXDivs = lattice.fYDivs = divs;
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001001
1002 // Don't care what these record, as long as they're legal.
1003 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001004 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001005 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -08001006 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
msarettc573a402016-08-02 08:05:56 -07001007 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001008}
1009
1010static void test_draw_bitmaps(SkCanvas* canvas) {
1011 SkBitmap empty;
1012 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001013 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001014 draw_bitmaps(empty, canvas);
1015}
1016
1017DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001018 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001019 test_draw_bitmaps(recorder.beginRecording(10, 10));
reedca2622b2016-03-18 07:25:55 -07001020 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001021}
1022
1023DEF_TEST(Canvas_EmptyBitmap, r) {
1024 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001025 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001026 SkCanvas canvas(dst);
1027
1028 test_draw_bitmaps(&canvas);
1029}
dneto3f22e8c2014-07-30 15:42:22 -07001030
1031DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1032 // This test is from crbug.com/344987.
1033 // The commands are:
1034 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001035 // drawBitmapRect
1036 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001037 // restore
1038 // The bug was that this structure was modified so that:
1039 // - The saveLayer and restore were eliminated
1040 // - The alpha was only applied to the first drawBitmapRectToRect
1041
1042 // This test draws blue and red squares inside a 50% transparent
1043 // layer. Both colours should show up muted.
1044 // When the bug is present, the red square (the second bitmap)
1045 // shows upwith full opacity.
1046
1047 SkBitmap blueBM;
1048 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1049 SkBitmap redBM;
1050 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1051 SkPaint semiTransparent;
1052 semiTransparent.setAlpha(0x80);
1053
1054 SkPictureRecorder recorder;
1055 SkCanvas* canvas = recorder.beginRecording(100, 100);
1056 canvas->drawARGB(0, 0, 0, 0);
1057
1058 canvas->saveLayer(0, &semiTransparent);
1059 canvas->drawBitmap(blueBM, 25, 25);
1060 canvas->drawBitmap(redBM, 50, 50);
1061 canvas->restore();
1062
reedca2622b2016-03-18 07:25:55 -07001063 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
dneto3f22e8c2014-07-30 15:42:22 -07001064
1065 // Now replay the picture back on another canvas
1066 // and check a couple of its pixels.
1067 SkBitmap replayBM;
1068 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1069 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001070 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001071 replayCanvas.flush();
1072
1073 // With the bug present, at (55, 55) we would get a fully opaque red
1074 // intead of a dark red.
1075 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1076 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1077}
mtklein3e8232b2014-08-18 13:39:11 -07001078
1079struct CountingBBH : public SkBBoxHierarchy {
1080 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001081 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001082
schenney23d85932015-03-06 16:20:28 -08001083 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001084
mtkleinc6ad06a2015-08-19 09:51:00 -07001085 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001086 this->searchCalls++;
1087 }
1088
mtklein36352bf2015-03-25 18:17:31 -07001089 void insert(const SkRect[], int) override {}
1090 virtual size_t bytesUsed() const override { return 0; }
1091 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001092};
1093
1094class SpoonFedBBHFactory : public SkBBHFactory {
1095public:
1096 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001097 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001098 return SkRef(fBBH);
1099 }
1100private:
1101 SkBBoxHierarchy* fBBH;
1102};
1103
1104// When the canvas clip covers the full picture, we don't need to call the BBH.
1105DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001106 SkRect bound = SkRect::MakeWH(320, 240);
1107 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001108 SpoonFedBBHFactory factory(&bbh);
1109
1110 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001111 SkCanvas* c = recorder.beginRecording(bound, &factory);
1112 // Record a few ops so we don't hit a small- or empty- picture optimization.
1113 c->drawRect(bound, SkPaint());
1114 c->drawRect(bound, SkPaint());
reedca2622b2016-03-18 07:25:55 -07001115 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
mtklein3e8232b2014-08-18 13:39:11 -07001116
1117 SkCanvas big(640, 480), small(300, 200);
1118
robertphillipsc5ba71d2014-09-04 08:42:50 -07001119 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001120 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1121
robertphillipsc5ba71d2014-09-04 08:42:50 -07001122 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001123 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1124}
mtkleind72094d2014-08-27 12:12:23 -07001125
1126DEF_TEST(Picture_BitmapLeak, r) {
1127 SkBitmap mut, immut;
1128 mut.allocN32Pixels(300, 200);
1129 immut.allocN32Pixels(300, 200);
1130 immut.setImmutable();
1131 SkASSERT(!mut.isImmutable());
1132 SkASSERT(immut.isImmutable());
1133
1134 // No one can hold a ref on our pixels yet.
1135 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1136 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1137
reedca2622b2016-03-18 07:25:55 -07001138 sk_sp<SkPicture> pic;
reed1bdfd3f2014-11-24 14:41:51 -08001139 {
1140 // we want the recorder to go out of scope before our subsequent checks, so we
1141 // place it inside local braces.
1142 SkPictureRecorder rec;
1143 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1144 canvas->drawBitmap(mut, 0, 0);
1145 canvas->drawBitmap(immut, 800, 600);
reedca2622b2016-03-18 07:25:55 -07001146 pic = rec.finishRecordingAsPicture();
reed1bdfd3f2014-11-24 14:41:51 -08001147 }
mtkleind72094d2014-08-27 12:12:23 -07001148
1149 // The picture shares the immutable pixels but copies the mutable ones.
1150 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1151 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1152
1153 // When the picture goes away, it's just our bitmaps holding the refs.
reedca2622b2016-03-18 07:25:55 -07001154 pic = nullptr;
mtkleind72094d2014-08-27 12:12:23 -07001155 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1156 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1157}
mtkleinfeaadee2015-04-08 11:25:48 -07001158
1159// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1160DEF_TEST(Picture_getRecordingCanvas, r) {
1161 SkPictureRecorder rec;
1162 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1163 for (int i = 0; i < 3; i++) {
1164 rec.beginRecording(100, 100);
1165 REPORTER_ASSERT(r, rec.getRecordingCanvas());
reedca2622b2016-03-18 07:25:55 -07001166 rec.finishRecordingAsPicture();
mtkleinfeaadee2015-04-08 11:25:48 -07001167 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1168 }
1169}
mtklein9db912c2015-05-19 11:11:26 -07001170
1171DEF_TEST(MiniRecorderLeftHanging, r) {
1172 // Any shader or other ref-counted effect will do just fine here.
1173 SkPaint paint;
reed1a9b9642016-03-13 14:13:58 -07001174 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
mtklein9db912c2015-05-19 11:11:26 -07001175
1176 SkMiniRecorder rec;
1177 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1178 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1179}
fmalita2ecc0002015-07-14 13:12:25 -07001180
1181DEF_TEST(Picture_preserveCullRect, r) {
1182 SkPictureRecorder recorder;
1183
1184 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1185 c->clear(SK_ColorCYAN);
1186
reedca2622b2016-03-18 07:25:55 -07001187 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
fmalita2ecc0002015-07-14 13:12:25 -07001188 SkDynamicMemoryWStream wstream;
1189 picture->serialize(&wstream);
1190
1191 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
reedca2622b2016-03-18 07:25:55 -07001192 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream));
fmalita2ecc0002015-07-14 13:12:25 -07001193
mtklein5f939ab2016-03-16 10:28:35 -07001194 REPORTER_ASSERT(r, deserializedPicture != nullptr);
fmalita2ecc0002015-07-14 13:12:25 -07001195 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1196 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1197 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1198 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1199}
fmalita796e3652016-05-13 11:40:07 -07001200
1201#if SK_SUPPORT_GPU
1202
1203DEF_TEST(PictureGpuAnalyzer, r) {
1204 SkPictureRecorder recorder;
1205
1206 {
1207 SkCanvas* canvas = recorder.beginRecording(10, 10);
1208 SkPaint paint;
1209 SkScalar intervals [] = { 10, 20 };
1210 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1211
1212 for (int i = 0; i < 50; ++i) {
1213 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1214 }
1215 }
1216 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1217
1218 SkPictureGpuAnalyzer analyzer;
1219 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1220
fmalita019db3f2016-05-31 06:32:57 -07001221 analyzer.analyzePicture(vetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001222 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1223
1224 analyzer.reset();
1225 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1226
1227 recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1228 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1229
fmalita019db3f2016-05-31 06:32:57 -07001230 analyzer.analyzePicture(nestedVetoPicture.get());
fmalita796e3652016-05-13 11:40:07 -07001231 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalitab5fc58e2016-05-25 11:31:04 -07001232
1233 analyzer.reset();
1234
1235 const SkPath convexClip = make_convex_path();
1236 const SkPath concaveClip = make_concave_path();
1237 for (int i = 0; i < 50; ++i) {
1238 analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, false);
1239 analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, true);
1240 analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, false);
1241 }
1242 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1243
1244 for (int i = 0; i < 50; ++i) {
1245 analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, true);
1246 }
1247 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
fmalita796e3652016-05-13 11:40:07 -07001248}
1249
1250#endif // SK_SUPPORT_GPU
reed41c27e12016-07-06 09:29:16 -07001251
1252///////////////////////////////////////////////////////////////////////////////////////////////////
1253
reedde996a02016-07-20 11:24:51 -07001254// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1255#if 0
reed41c27e12016-07-06 09:29:16 -07001256static void empty_ops(SkCanvas* canvas) {
1257}
1258static void clip_ops(SkCanvas* canvas) {
1259 canvas->save();
1260 canvas->clipRect(SkRect::MakeWH(20, 20));
1261 canvas->restore();
1262}
1263static void matrix_ops(SkCanvas* canvas) {
1264 canvas->save();
1265 canvas->scale(2, 3);
1266 canvas->restore();
1267}
1268static void matrixclip_ops(SkCanvas* canvas) {
1269 canvas->save();
1270 canvas->scale(2, 3);
1271 canvas->clipRect(SkRect::MakeWH(20, 20));
1272 canvas->restore();
1273}
1274typedef void (*CanvasProc)(SkCanvas*);
1275
1276// Test the kReturnNullForEmpty_FinishFlag option when recording
1277//
1278DEF_TEST(Picture_RecordEmpty, r) {
1279 const SkRect cull = SkRect::MakeWH(100, 100);
1280
1281 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1282
1283 for (auto proc : procs) {
1284 {
1285 SkPictureRecorder rec;
1286 proc(rec.beginRecording(cull));
1287 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1288 REPORTER_ASSERT(r, pic.get());
1289 REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1290 }
1291 {
1292 SkPictureRecorder rec;
1293 proc(rec.beginRecording(cull));
1294 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1295 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1296 REPORTER_ASSERT(r, !pic.get());
1297 }
1298 {
1299 SkPictureRecorder rec;
1300 proc(rec.beginRecording(cull));
1301 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1302 REPORTER_ASSERT(r, dr.get());
1303 }
1304 {
1305 SkPictureRecorder rec;
1306 proc(rec.beginRecording(cull));
1307 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1308 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1309 REPORTER_ASSERT(r, !dr.get());
1310 }
1311 }
1312}
reedde996a02016-07-20 11:24:51 -07001313#endif
1314