blob: 45edc715efc2b67ce421b8194559469418586141 [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
mtklein3e8232b2014-08-18 13:39:11 -07008#include "SkBBoxHierarchy.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +00009#include "SkBlurImageFilter.h"
reed@google.com21b519d2012-10-02 17:42:15 +000010#include "SkCanvas.h"
robertphillipsd8aa7b72014-10-30 16:45:02 -070011#include "SkColorMatrixFilter.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000012#include "SkColorPriv.h"
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +000013#include "SkDashPathEffect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000014#include "SkData.h"
reed5965c8a2015-01-07 18:04:45 -080015#include "SkImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000016#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000017#include "SkImageEncoder.h"
18#include "SkImageGenerator.h"
robertphillips82365912014-11-12 09:32:34 -080019#include "SkLayerInfo.h"
reed@google.com21b519d2012-10-02 17:42:15 +000020#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000021#include "SkPicture.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000022#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000023#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070024#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080025#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070026#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000027#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000028#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080029#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000030#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000031#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070032#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000033
34#if SK_SUPPORT_GPU
35#include "SkSurface.h"
36#include "GrContextFactory.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000037#endif
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000038#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000039
reed@google.com47b679b2014-05-14 18:58:16 +000040#include "SkLumaColorFilter.h"
41#include "SkColorFilterImageFilter.h"
42
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000044 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000045 bm->eraseColor(color);
46 if (immutable) {
47 bm->setImmutable();
48 }
49}
50
mtkleina16af212015-08-26 08:14:52 -070051// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
52static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
53 // We just need _some_ SkImage.
54 SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(SkBitmap()));
55
56 SkPictureRecorder recorder;
57 {
58 auto canvas = recorder.beginRecording(100,100);
59 canvas->drawImage(image, 0,0);
60 }
61 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
62
63 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
64}
65
tomhudson3a0f2792014-08-20 05:29:41 -070066/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070067static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070068 SkPictureRecorder recorder;
69
mtklein8e126562014-10-01 09:29:35 -070070 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070071 {
72 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
73 }
74 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
75 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
76
mtklein8e126562014-10-01 09:29:35 -070077 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070078 {
79 SkPaint paint;
80 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
81 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
82 SkBitmap bitmap;
83 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
84 bitmap.eraseColor(SK_ColorBLUE);
85 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
86 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
87 SkShader::kClamp_TileMode);
88 paint.setShader(shader)->unref();
reedf5822822015-08-19 11:46:38 -070089 REPORTER_ASSERT(reporter, shader->isABitmap());
tomhudson3a0f2792014-08-20 05:29:41 -070090
91 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
92 }
93 picture.reset(recorder.endRecording());
94 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
95}
96
97
scroggo@google.comd614c6a2012-09-14 17:26:37 +000098#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070099// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -0700100// in debug mode, so only run in debug mode.
101static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000102 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000103 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700104 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -0700105 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000106 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -0700107 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700108 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000109}
110
111// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
112static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000113 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700114 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000115 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000116 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000117 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000118}
119#endif
120
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000121static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000122 SkPaint paint;
123 SkRect rect = SkRect::MakeWH(50, 50);
124
125 SkScalar unit = rand.nextUScalar1();
126 if (unit <= 0.3) {
127// SkDebugf("save\n");
128 canvas->save();
129 } else if (unit <= 0.6) {
130// SkDebugf("restore\n");
131 canvas->restore();
132 } else if (unit <= 0.9) {
133// SkDebugf("clip\n");
134 canvas->clipRect(rect);
135 } else {
136// SkDebugf("draw\n");
137 canvas->drawPaint(paint);
138 }
139}
140
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000141#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700142
mtklein8e126562014-10-01 09:29:35 -0700143static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000144 SkPictureRecorder recorder;
145
mtklein8e126562014-10-01 09:29:35 -0700146 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000147 {
148 SkPath path;
149 path.moveTo(0, 0);
150 path.lineTo(50, 50);
151
152 SkScalar intervals[] = { 1.0f, 1.0f };
153 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
154
155 SkPaint paint;
156 paint.setStyle(SkPaint::kStroke_Style);
157 paint.setPathEffect(dash);
158
robertphillips98b03152015-01-26 11:29:36 -0800159 for (int i = 0; i < 50; ++i) {
160 canvas->drawPath(path, paint);
161 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000162 }
163 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
164 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000165
halcanary96fcdcc2015-08-27 07:41:13 -0700166 const char *reason = nullptr;
167 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700168 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000169
mtklein8e126562014-10-01 09:29:35 -0700170 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000171 {
172 SkPath path;
173
174 path.moveTo(0, 0);
175 path.lineTo(0, 50);
176 path.lineTo(25, 25);
177 path.lineTo(50, 50);
178 path.lineTo(50, 0);
179 path.close();
180 REPORTER_ASSERT(reporter, !path.isConvex());
181
182 SkPaint paint;
183 paint.setAntiAlias(true);
184 for (int i = 0; i < 50; ++i) {
185 canvas->drawPath(path, paint);
186 }
187 }
188 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800189 // A lot of small AA concave paths should be fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700190 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
jvanverthd86b07a2014-11-04 08:50:15 -0800191
192 canvas = recorder.beginRecording(100, 100);
193 {
194 SkPath path;
195
196 path.moveTo(0, 0);
197 path.lineTo(0, 100);
198 path.lineTo(50, 50);
199 path.lineTo(100, 100);
200 path.lineTo(100, 0);
201 path.close();
202 REPORTER_ASSERT(reporter, !path.isConvex());
203
204 SkPaint paint;
205 paint.setAntiAlias(true);
206 for (int i = 0; i < 50; ++i) {
207 canvas->drawPath(path, paint);
208 }
209 }
210 picture.reset(recorder.endRecording());
211 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700212 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000213
mtklein8e126562014-10-01 09:29:35 -0700214 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000215 {
216 SkPath path;
217
218 path.moveTo(0, 0);
219 path.lineTo(0, 50);
220 path.lineTo(25, 25);
221 path.lineTo(50, 50);
222 path.lineTo(50, 0);
223 path.close();
224 REPORTER_ASSERT(reporter, !path.isConvex());
225
226 SkPaint paint;
227 paint.setAntiAlias(true);
228 paint.setStyle(SkPaint::kStroke_Style);
229 paint.setStrokeWidth(0);
230 for (int i = 0; i < 50; ++i) {
231 canvas->drawPath(path, paint);
232 }
233 }
234 picture.reset(recorder.endRecording());
235 // hairline stroked AA concave paths are fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700236 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700237
mtklein8e126562014-10-01 09:29:35 -0700238 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700239 {
240 SkPaint paint;
241 SkScalar intervals [] = { 10, 20 };
242 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
243 paint.setPathEffect(pe)->unref();
244
245 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800246
247 for (int i = 0; i < 50; ++i) {
248 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
249 }
tomhudson3a0f2792014-08-20 05:29:41 -0700250 }
251 picture.reset(recorder.endRecording());
252 // fast-path dashed effects are fine for GPU rendering ...
halcanary96fcdcc2015-08-27 07:41:13 -0700253 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700254
mtklein8e126562014-10-01 09:29:35 -0700255 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700256 {
257 SkPaint paint;
258 SkScalar intervals [] = { 10, 20 };
259 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
260 paint.setPathEffect(pe)->unref();
261
robertphillips98b03152015-01-26 11:29:36 -0800262 for (int i = 0; i < 50; ++i) {
263 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
264 }
tomhudson3a0f2792014-08-20 05:29:41 -0700265 }
266 picture.reset(recorder.endRecording());
267 // ... but only when applied to drawPoint() calls
halcanary96fcdcc2015-08-27 07:41:13 -0700268 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
mtklein53fecfb2014-08-21 09:11:37 -0700269
270 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700271 canvas = recorder.beginRecording(100, 100);
272 {
273 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700274 }
mtklein8e126562014-10-01 09:29:35 -0700275 picture.reset(recorder.endRecording());
halcanary96fcdcc2015-08-27 07:41:13 -0700276 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000277}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000278
robertphillips82365912014-11-12 09:32:34 -0800279#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000280
robertphillips82365912014-11-12 09:32:34 -0800281static void test_savelayer_extraction(skiatest::Reporter* reporter) {
282 static const int kWidth = 100;
283 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000284
robertphillips82365912014-11-12 09:32:34 -0800285 // Create complex paint that the bounding box computation code can't
286 // optimize away
287 SkScalar blueToRedMatrix[20] = { 0 };
288 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
289 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
290 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000291
robertphillips82365912014-11-12 09:32:34 -0800292 SkPaint complexPaint;
293 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000294
robertphillips82365912014-11-12 09:32:34 -0800295 SkAutoTUnref<SkPicture> pict, child;
296 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000297
robertphillips82365912014-11-12 09:32:34 -0800298 {
299 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700300
robertphillips82365912014-11-12 09:32:34 -0800301 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
302 &bbhFactory,
303 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700304
halcanary96fcdcc2015-08-27 07:41:13 -0700305 c->saveLayer(nullptr, &complexPaint);
robertphillips82365912014-11-12 09:32:34 -0800306 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700307
robertphillips82365912014-11-12 09:32:34 -0800308 child.reset(recorder.endRecording());
309 }
robertphillipsd6283302014-08-27 11:53:28 -0700310
robertphillips82365912014-11-12 09:32:34 -0800311 // create a picture with the structure:
312 // 1)
313 // SaveLayer
314 // Restore
315 // 2)
316 // SaveLayer
317 // Translate
318 // SaveLayer w/ bound
319 // Restore
320 // Restore
321 // 3)
322 // SaveLayer w/ copyable paint
323 // Restore
324 // 4)
325 // SaveLayer
326 // DrawPicture (which has a SaveLayer/Restore pair)
327 // Restore
328 // 5)
329 // SaveLayer
330 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
331 // Restore
332 {
333 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700334
robertphillips82365912014-11-12 09:32:34 -0800335 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
336 SkIntToScalar(kHeight),
337 &bbhFactory,
338 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000339 // 1)
halcanary96fcdcc2015-08-27 07:41:13 -0700340 c->saveLayer(nullptr, &complexPaint); // layer #0
robertphillips82365912014-11-12 09:32:34 -0800341 c->restore();
342
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000343 // 2)
halcanary96fcdcc2015-08-27 07:41:13 -0700344 c->saveLayer(nullptr, nullptr); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800345 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800346 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
347 c->saveLayer(&r, &complexPaint); // layer #2
348 c->restore();
349 c->restore();
350
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000351 // 3)
robertphillips82365912014-11-12 09:32:34 -0800352 {
halcanary96fcdcc2015-08-27 07:41:13 -0700353 c->saveLayer(nullptr, &complexPaint); // layer #3
robertphillips82365912014-11-12 09:32:34 -0800354 c->restore();
355 }
356
357 SkPaint layerPaint;
358 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000359 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000360 {
halcanary96fcdcc2015-08-27 07:41:13 -0700361 c->saveLayer(nullptr, &layerPaint); // layer #4
robertphillips82365912014-11-12 09:32:34 -0800362 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000363 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800364 }
365 // 5
366 {
367 SkPaint picturePaint;
368 SkMatrix trans;
369 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700370
halcanary96fcdcc2015-08-27 07:41:13 -0700371 c->saveLayer(nullptr, &layerPaint); // layer #6
robertphillips82365912014-11-12 09:32:34 -0800372 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700373 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000374 }
375
robertphillips82365912014-11-12 09:32:34 -0800376 pict.reset(recorder.endRecording());
377 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000378
robertphillips82365912014-11-12 09:32:34 -0800379 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800380 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
mtklein9db912c2015-05-19 11:11:26 -0700381 const SkBigPicture* bp = pict->asSkBigPicture();
382 REPORTER_ASSERT(reporter, bp);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000383
mtklein9db912c2015-05-19 11:11:26 -0700384 const SkBigPicture::AccelData* data = bp->accelData();
robertphillips82365912014-11-12 09:32:34 -0800385 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000386
robertphillips82365912014-11-12 09:32:34 -0800387 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
388 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700389
robertphillips82365912014-11-12 09:32:34 -0800390 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
391 // The parent/child layers appear in reverse order
392 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
393 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700394
robertphillips82365912014-11-12 09:32:34 -0800395 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700396
robertphillips82365912014-11-12 09:32:34 -0800397 // The parent/child layers appear in reverse order
398 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
399 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000400
robertphillips82365912014-11-12 09:32:34 -0800401 // The parent/child layers appear in reverse order
402 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
403 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000404
halcanary96fcdcc2015-08-27 07:41:13 -0700405 REPORTER_ASSERT(reporter, nullptr == info0.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800406 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
407 kHeight == info0.fBounds.height());
408 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
409 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
410 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700411 REPORTER_ASSERT(reporter, nullptr != info0.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800412 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000413
halcanary96fcdcc2015-08-27 07:41:13 -0700414 REPORTER_ASSERT(reporter, nullptr == info1.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800415 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
416 kHeight/2.0 == info1.fBounds.height());
417 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
418 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800419 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800420 kHeight/2.0 == info1.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700421 REPORTER_ASSERT(reporter, nullptr == info1.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800422 REPORTER_ASSERT(reporter, !info1.fIsNested &&
423 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000424
halcanary96fcdcc2015-08-27 07:41:13 -0700425 REPORTER_ASSERT(reporter, nullptr == info2.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800426 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
427 kHeight / 2 == info2.fBounds.height()); // bound reduces size
428 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
429 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
430 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
431 kHeight / 2 == info2.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700432 REPORTER_ASSERT(reporter, nullptr != info2.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800433 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000434
halcanary96fcdcc2015-08-27 07:41:13 -0700435 REPORTER_ASSERT(reporter, nullptr == info3.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800436 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
437 kHeight == info3.fBounds.height());
438 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
439 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
440 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
441 REPORTER_ASSERT(reporter, info3.fPaint);
442 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700443
halcanary96fcdcc2015-08-27 07:41:13 -0700444 REPORTER_ASSERT(reporter, nullptr == info4.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800445 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
446 kHeight == info4.fBounds.height());
447 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
448 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
449 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
450 REPORTER_ASSERT(reporter, info4.fPaint);
451 REPORTER_ASSERT(reporter, !info4.fIsNested &&
452 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700453
robertphillips82365912014-11-12 09:32:34 -0800454 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
455 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
456 kHeight == info5.fBounds.height());
457 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
458 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
459 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700460 REPORTER_ASSERT(reporter, nullptr != info5.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800461 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700462
halcanary96fcdcc2015-08-27 07:41:13 -0700463 REPORTER_ASSERT(reporter, nullptr == info6.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800464 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
465 kHeight-10 == info6.fBounds.height());
466 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
467 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
468 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
469 REPORTER_ASSERT(reporter, info6.fPaint);
470 REPORTER_ASSERT(reporter, !info6.fIsNested &&
471 info6.fHasNestedLayers); // has a nested SL
472
473 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
474 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
475 kHeight == info7.fBounds.height());
476 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
477 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
478 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700479 REPORTER_ASSERT(reporter, nullptr != info7.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800480 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000481 }
482}
483
mtklein8e126562014-10-01 09:29:35 -0700484static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700485 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700486
mtklein8e126562014-10-01 09:29:35 -0700487 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700488 {
mtkleinc551d9f2014-08-20 08:09:46 -0700489 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700490 }
491 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
492 REPORTER_ASSERT(reporter, !picture->hasText());
493
mtkleinc551d9f2014-08-20 08:09:46 -0700494 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700495 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700496 {
mtkleinc551d9f2014-08-20 08:09:46 -0700497 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700498 }
499 picture.reset(recorder.endRecording());
500 REPORTER_ASSERT(reporter, picture->hasText());
501
mtklein8e126562014-10-01 09:29:35 -0700502 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700503 {
mtkleinc551d9f2014-08-20 08:09:46 -0700504 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700505 }
506 picture.reset(recorder.endRecording());
507 REPORTER_ASSERT(reporter, picture->hasText());
508
mtklein8e126562014-10-01 09:29:35 -0700509 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700510 {
mtkleinc551d9f2014-08-20 08:09:46 -0700511 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700512 }
513 picture.reset(recorder.endRecording());
514 REPORTER_ASSERT(reporter, picture->hasText());
515
mtklein8e126562014-10-01 09:29:35 -0700516 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700517 {
518 SkPath path;
519 path.moveTo(0, 0);
520 path.lineTo(50, 50);
521
mtkleinc551d9f2014-08-20 08:09:46 -0700522 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700523 }
524 picture.reset(recorder.endRecording());
525 REPORTER_ASSERT(reporter, picture->hasText());
526
mtklein8e126562014-10-01 09:29:35 -0700527 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700528 {
529 SkPath path;
530 path.moveTo(0, 0);
531 path.lineTo(50, 50);
532
halcanary96fcdcc2015-08-27 07:41:13 -0700533 canvas->drawTextOnPath("Q", 1, path, nullptr, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700534 }
535 picture.reset(recorder.endRecording());
536 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700537
538 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700539 canvas = recorder.beginRecording(100,100);
540 {
541 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700542 }
mtklein8e126562014-10-01 09:29:35 -0700543 picture.reset(recorder.endRecording());
544 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700545}
546
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000547static void set_canvas_to_save_count_4(SkCanvas* canvas) {
548 canvas->restoreToCount(1);
549 canvas->save();
550 canvas->save();
551 canvas->save();
552}
553
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000554/**
555 * A canvas that records the number of saves, saveLayers and restores.
556 */
557class SaveCountingCanvas : public SkCanvas {
558public:
559 SaveCountingCanvas(int width, int height)
560 : INHERITED(width, height)
561 , fSaveCount(0)
562 , fSaveLayerCount(0)
563 , fRestoreCount(0){
564 }
565
566 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
mtklein36352bf2015-03-25 18:17:31 -0700567 SaveFlags flags) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000568 ++fSaveLayerCount;
569 return this->INHERITED::willSaveLayer(bounds, paint, flags);
570 }
571
mtklein36352bf2015-03-25 18:17:31 -0700572 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000573 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400574 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000575 }
576
mtklein36352bf2015-03-25 18:17:31 -0700577 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000578 ++fRestoreCount;
579 this->INHERITED::willRestore();
580 }
581
582 unsigned int getSaveCount() const { return fSaveCount; }
583 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
584 unsigned int getRestoreCount() const { return fRestoreCount; }
585
586private:
587 unsigned int fSaveCount;
588 unsigned int fSaveLayerCount;
589 unsigned int fRestoreCount;
590
591 typedef SkCanvas INHERITED;
592};
593
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000594void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000595 unsigned int numSaves, unsigned int numSaveLayers,
596 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700597 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700598 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000599
robertphillipsc5ba71d2014-09-04 08:42:50 -0700600 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000601
mtklein87c41382014-09-08 07:31:18 -0700602 // Optimizations may have removed these,
603 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
604 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
605 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
606 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000607}
608
609// This class exists so SkPicture can friend it and give it access to
610// the 'partialReplay' method.
611class SkPictureRecorderReplayTester {
612public:
613 static SkPicture* Copy(SkPictureRecorder* recorder) {
614 SkPictureRecorder recorder2;
615
robertphillips9f1c2412014-06-09 06:25:34 -0700616 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000617
618 recorder->partialReplay(canvas);
619
620 return recorder2.endRecording();
621 }
622};
623
robertphillips9058d602014-06-10 11:45:46 -0700624static void create_imbalance(SkCanvas* canvas) {
625 SkRect clipRect = SkRect::MakeWH(2, 2);
626 SkRect drawRect = SkRect::MakeWH(10, 10);
627 canvas->save();
628 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
629 canvas->translate(1.0f, 1.0f);
630 SkPaint p;
631 p.setColor(SK_ColorGREEN);
632 canvas->drawRect(drawRect, p);
633 // no restore
634}
635
636// This tests that replaying a potentially unbalanced picture into a canvas
637// doesn't affect the canvas' save count or matrix/clip state.
638static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
639 SkBitmap bm;
640 bm.allocN32Pixels(4, 3);
641 SkCanvas canvas(bm);
642
643 int beforeSaveCount = canvas.getSaveCount();
644
645 SkMatrix beforeMatrix = canvas.getTotalMatrix();
646
647 SkRect beforeClip;
648
649 canvas.getClipBounds(&beforeClip);
650
651 canvas.drawPicture(picture);
652
653 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
654 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
655
656 SkRect afterClip;
657
658 canvas.getClipBounds(&afterClip);
659
660 REPORTER_ASSERT(reporter, afterClip == beforeClip);
661}
662
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000663// Test out SkPictureRecorder::partialReplay
664DEF_TEST(PictureRecorder_replay, reporter) {
665 // check save/saveLayer state
666 {
667 SkPictureRecorder recorder;
668
robertphillips9f1c2412014-06-09 06:25:34 -0700669 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000670
halcanary96fcdcc2015-08-27 07:41:13 -0700671 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000672
673 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
674
675 // The extra save and restore comes from the Copy process.
676 check_save_state(reporter, copy, 2, 1, 3);
677
halcanary96fcdcc2015-08-27 07:41:13 -0700678 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000679
680 SkAutoTUnref<SkPicture> final(recorder.endRecording());
681
682 check_save_state(reporter, final, 1, 2, 3);
683
684 // The copy shouldn't pick up any operations added after it was made
685 check_save_state(reporter, copy, 2, 1, 3);
686 }
687
688 // (partially) check leakage of draw ops
689 {
690 SkPictureRecorder recorder;
691
robertphillips9f1c2412014-06-09 06:25:34 -0700692 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000693
694 SkRect r = SkRect::MakeWH(5, 5);
695 SkPaint p;
696
697 canvas->drawRect(r, p);
698
699 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
700
701 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
702
703 SkBitmap bm;
704 make_bm(&bm, 10, 10, SK_ColorRED, true);
705
706 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700707 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000708
709 SkAutoTUnref<SkPicture> final(recorder.endRecording());
710 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
711
712 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
713
714 // The snapshot shouldn't pick up any operations added after it was made
715 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
716 }
robertphillips9058d602014-06-10 11:45:46 -0700717
718 // Recreate the Android partialReplay test case
719 {
720 SkPictureRecorder recorder;
721
halcanary96fcdcc2015-08-27 07:41:13 -0700722 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700723 create_imbalance(canvas);
724
725 int expectedSaveCount = canvas->getSaveCount();
726
727 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
728 check_balance(reporter, copy);
729
730 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
731
732 // End the recording of source to test the picture finalization
733 // process isn't complicated by the partialReplay step
734 SkAutoTUnref<SkPicture> final(recorder.endRecording());
735 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000736}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000737
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000738static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
739 SkCanvas testCanvas(100, 100);
740 set_canvas_to_save_count_4(&testCanvas);
741
742 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
743
744 SkPaint paint;
745 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
746
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000747 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000748
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000749 {
750 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700751 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000752 canvas->save();
753 canvas->translate(10, 10);
754 canvas->drawRect(rect, paint);
755 canvas->save();
756 canvas->translate(10, 10);
757 canvas->drawRect(rect, paint);
758 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
759
robertphillips9b14f262014-06-04 05:40:44 -0700760 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000761 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
762 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000763
764 set_canvas_to_save_count_4(&testCanvas);
765
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000766 {
767 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700768 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000769 canvas->save();
770 canvas->translate(10, 10);
771 canvas->drawRect(rect, paint);
772 canvas->save();
773 canvas->translate(10, 10);
774 canvas->drawRect(rect, paint);
775 canvas->restore();
776 canvas->restore();
777 canvas->restore();
778 canvas->restore();
779 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000780
robertphillips9b14f262014-06-04 05:40:44 -0700781 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000782 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
783 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000784
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000785 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000786
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000787 {
robertphillips9f1c2412014-06-09 06:25:34 -0700788 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000789 canvas->translate(10, 10);
790 canvas->drawRect(rect, paint);
791 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
792
robertphillips9b14f262014-06-04 05:40:44 -0700793 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000794 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
795 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
796 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000797}
798
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000799static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000800 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000801
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000802 SkPictureRecorder recorder;
803
reed@google.com21b519d2012-10-02 17:42:15 +0000804 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000805 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000806
robertphillips9f1c2412014-06-09 06:25:34 -0700807 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000808
809 for (int i = 0; i < 1000; ++i) {
810 rand_op(canvas, rand);
811 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000812 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000813
814 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000815 }
816
817 {
robertphillips9f1c2412014-06-09 06:25:34 -0700818 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000819 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000820
reed@google.com21b519d2012-10-02 17:42:15 +0000821 for (int i = 0; i < 100; ++i) {
822 canvas->save();
823 }
824 while (canvas->getSaveCount() > 1) {
825 canvas->clipRect(rect);
826 canvas->restore();
827 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000828 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000829 }
830}
831
scroggo@google.com4b90b112012-12-04 15:08:56 +0000832#ifndef SK_DEBUG
833// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
834// should never do this.
835static void test_bad_bitmap() {
836 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
837 // fail.
838 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000839 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000840 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700841 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000842 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000843 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000844
845 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700846 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000847}
848#endif
849
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000850static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000851 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700852 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700853 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000854 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000855 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
856
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000857 SkDynamicMemoryWStream wStream;
robertphillips3e5c2b12015-03-23 05:46:51 -0700858 sk_tool_utils::PngPixelSerializer serializer;
scroggo895c43b2014-12-11 10:53:58 -0800859 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000860 return wStream.copyToData();
861}
862
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000863struct ErrorContext {
864 int fErrors;
865 skiatest::Reporter* fReporter;
866};
867
868static void assert_one_parse_error_cb(SkError error, void* context) {
869 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
870 errorContext->fErrors++;
871 // This test only expects one error, and that is a kParseError. If there are others,
872 // there is some unknown problem.
873 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
874 "This threw more errors than expected.");
875 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
876 SkGetLastErrorString());
877}
878
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000879static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
880 // Create a bitmap that will be encoded.
881 SkBitmap original;
882 make_bm(&original, 100, 100, SK_ColorBLUE, true);
883 SkDynamicMemoryWStream wStream;
884 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
885 return;
886 }
887 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000888
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000889 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800890 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000891 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000892
893 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
894 // Flattening original will follow the old path of performing an encode, while flattening bm
895 // will use the already encoded data.
896 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
897 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
898 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000899 // Now test that a parse error was generated when trying to create a new SkPicture without
900 // providing a function to decode the bitmap.
901 ErrorContext context;
902 context.fErrors = 0;
903 context.fReporter = reporter;
904 SkSetErrorCallback(assert_one_parse_error_cb, &context);
905 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000906 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700907 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, nullptr));
908 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000909 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700910 SkSetErrorCallback(nullptr, nullptr);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000911}
912
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000913static void test_clip_bound_opt(skiatest::Reporter* reporter) {
914 // Test for crbug.com/229011
915 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
916 SkIntToScalar(2), SkIntToScalar(2));
917 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
918 SkIntToScalar(1), SkIntToScalar(1));
919 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
920 SkIntToScalar(1), SkIntToScalar(1));
921
922 SkPath invPath;
923 invPath.addOval(rect1);
924 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
925 SkPath path;
926 path.addOval(rect2);
927 SkPath path2;
928 path2.addOval(rect3);
929 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000930 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700931
932 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000933 {
robertphillips9f1c2412014-06-09 06:25:34 -0700934 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000935 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
936 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
937 REPORTER_ASSERT(reporter, true == nonEmpty);
938 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
939 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
940 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
941 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
942 }
943 {
robertphillips9f1c2412014-06-09 06:25:34 -0700944 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000945 canvas->clipPath(path, SkRegion::kIntersect_Op);
946 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
947 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
948 REPORTER_ASSERT(reporter, true == nonEmpty);
949 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
950 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
951 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
952 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
953 }
954 {
robertphillips9f1c2412014-06-09 06:25:34 -0700955 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000956 canvas->clipPath(path, SkRegion::kIntersect_Op);
957 canvas->clipPath(invPath, SkRegion::kUnion_Op);
958 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
959 REPORTER_ASSERT(reporter, true == nonEmpty);
960 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
961 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
962 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
963 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
964 }
965 {
robertphillips9f1c2412014-06-09 06:25:34 -0700966 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000967 canvas->clipPath(path, SkRegion::kDifference_Op);
968 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
969 REPORTER_ASSERT(reporter, true == nonEmpty);
970 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
971 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
972 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
973 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
974 }
975 {
robertphillips9f1c2412014-06-09 06:25:34 -0700976 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000977 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
978 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
979 // True clip is actually empty in this case, but the best
980 // determination we can make using only bounds as input is that the
981 // clip is included in the bounds of 'path'.
982 REPORTER_ASSERT(reporter, true == nonEmpty);
983 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
984 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
985 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
986 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
987 }
988 {
robertphillips9f1c2412014-06-09 06:25:34 -0700989 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000990 canvas->clipPath(path, SkRegion::kIntersect_Op);
991 canvas->clipPath(path2, SkRegion::kXOR_Op);
992 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
993 REPORTER_ASSERT(reporter, true == nonEmpty);
994 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
995 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
996 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
997 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
998 }
999}
1000
schenneyeeff8bb2015-07-07 14:27:10 -07001001static void test_cull_rect_reset(skiatest::Reporter* reporter) {
1002 SkPictureRecorder recorder;
1003 SkRect bounds = SkRect::MakeWH(10, 10);
1004 SkRTreeFactory factory;
1005 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
1006 bounds = SkRect::MakeWH(100, 100);
1007 SkPaint paint;
1008 canvas->drawRect(bounds, paint);
1009 canvas->drawRect(bounds, paint);
mtkleineedc3342015-07-08 08:26:39 -07001010 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
1011 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -07001012 REPORTER_ASSERT(reporter, picture);
1013
1014 SkRect finalCullRect = picture->cullRect();
1015 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1016 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1017 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1018 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1019
1020 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1021 SkRect bbhCullRect = pictureBBH->getRootBound();
1022 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1023 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1024 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1025 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1026}
1027
1028
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001029/**
1030 * A canvas that records the number of clip commands.
1031 */
1032class ClipCountingCanvas : public SkCanvas {
1033public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001034 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001035 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001036 , fClipCount(0){
1037 }
1038
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001039 virtual void onClipRect(const SkRect& r,
1040 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001041 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001042 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001043 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001044 }
1045
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001046 virtual void onClipRRect(const SkRRect& rrect,
1047 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001048 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001049 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001050 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001051 }
1052
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001053 virtual void onClipPath(const SkPath& path,
1054 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001055 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001056 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001057 this->INHERITED::onClipPath(path, op, edgeStyle);
1058 }
1059
mtklein36352bf2015-03-25 18:17:31 -07001060 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001061 fClipCount += 1;
1062 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001063 }
1064
1065 unsigned getClipCount() const { return fClipCount; }
1066
1067private:
1068 unsigned fClipCount;
1069
1070 typedef SkCanvas INHERITED;
1071};
1072
1073static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001074 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001075 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001076
1077 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1078 // The following expanding clip should not be skipped.
1079 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1080 // Draw something so the optimizer doesn't just fold the world.
1081 SkPaint p;
1082 p.setColor(SK_ColorBLUE);
1083 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001084 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001085
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001086 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001087 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001088
1089 // Both clips should be present on playback.
1090 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1091}
1092
tomhudson@google.com381010e2013-10-24 11:12:47 +00001093static void test_hierarchical(skiatest::Reporter* reporter) {
1094 SkBitmap bm;
1095 make_bm(&bm, 10, 10, SK_ColorRED, true);
1096
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001097 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001098
robertphillips9f1c2412014-06-09 06:25:34 -07001099 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001100 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1101 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001102
robertphillips9f1c2412014-06-09 06:25:34 -07001103 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001104 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1105 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001106
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001107 {
robertphillips9f1c2412014-06-09 06:25:34 -07001108 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001109 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001110 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1111 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1112 }
1113 {
robertphillips9f1c2412014-06-09 06:25:34 -07001114 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001115 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001116 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1117 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1118 }
1119 {
robertphillips9f1c2412014-06-09 06:25:34 -07001120 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001121 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001122 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001123 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1124 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1125 }
1126 {
robertphillips9f1c2412014-06-09 06:25:34 -07001127 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001128 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001129 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001130 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1131 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1132 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001133}
1134
robertphillips@google.comd5500882014-04-02 23:51:13 +00001135static void test_gen_id(skiatest::Reporter* reporter) {
1136
Robert Phillipscfaeec42014-07-13 12:00:50 -04001137 SkPictureRecorder recorder;
1138 recorder.beginRecording(0, 0);
1139 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001140
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001141 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001142 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001143
robertphillips9f1c2412014-06-09 06:25:34 -07001144 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001145 canvas->drawARGB(255, 255, 255, 255);
1146 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1147 // picture should have a non-zero id after recording
1148 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001149
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001150 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001151 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001152}
1153
caryclark5ef194c2015-08-31 09:22:38 -07001154static void test_typeface(skiatest::Reporter* reporter) {
1155 SkPictureRecorder recorder;
1156 SkCanvas* canvas = recorder.beginRecording(10, 10);
1157 SkPaint paint;
1158 paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic));
1159 canvas->drawText("Q", 1, 0, 10, paint);
1160 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1161 REPORTER_ASSERT(reporter, picture->hasText());
1162 SkDynamicMemoryWStream stream;
1163 picture->serialize(&stream);
1164}
1165
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001166DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -07001167 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001168#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001169 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001170 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001171#else
1172 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001173#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001174 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001175 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001176#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001177 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001178#endif
mtklein8e126562014-10-01 09:29:35 -07001179 test_has_text(reporter);
mtkleina16af212015-08-26 08:14:52 -07001180 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -07001181 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001182 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001183 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001184 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001185 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001186 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001187 test_savelayer_extraction(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -07001188 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001189}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001190
1191static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1192 const SkPaint paint;
1193 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1194 const SkIRect irect = { 2, 2, 3, 3 };
1195
1196 // Don't care what these record, as long as they're legal.
1197 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001198 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001199 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1200 canvas->drawSprite(bitmap, 1, 1);
1201}
1202
1203static void test_draw_bitmaps(SkCanvas* canvas) {
1204 SkBitmap empty;
1205 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001206 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001207 draw_bitmaps(empty, canvas);
1208}
1209
1210DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001211 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001212 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001213 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001214}
1215
1216DEF_TEST(Canvas_EmptyBitmap, r) {
1217 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001218 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001219 SkCanvas canvas(dst);
1220
1221 test_draw_bitmaps(&canvas);
1222}
dneto3f22e8c2014-07-30 15:42:22 -07001223
1224DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1225 // This test is from crbug.com/344987.
1226 // The commands are:
1227 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001228 // drawBitmapRect
1229 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001230 // restore
1231 // The bug was that this structure was modified so that:
1232 // - The saveLayer and restore were eliminated
1233 // - The alpha was only applied to the first drawBitmapRectToRect
1234
1235 // This test draws blue and red squares inside a 50% transparent
1236 // layer. Both colours should show up muted.
1237 // When the bug is present, the red square (the second bitmap)
1238 // shows upwith full opacity.
1239
1240 SkBitmap blueBM;
1241 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1242 SkBitmap redBM;
1243 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1244 SkPaint semiTransparent;
1245 semiTransparent.setAlpha(0x80);
1246
1247 SkPictureRecorder recorder;
1248 SkCanvas* canvas = recorder.beginRecording(100, 100);
1249 canvas->drawARGB(0, 0, 0, 0);
1250
1251 canvas->saveLayer(0, &semiTransparent);
1252 canvas->drawBitmap(blueBM, 25, 25);
1253 canvas->drawBitmap(redBM, 50, 50);
1254 canvas->restore();
1255
1256 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1257
1258 // Now replay the picture back on another canvas
1259 // and check a couple of its pixels.
1260 SkBitmap replayBM;
1261 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1262 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001263 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001264 replayCanvas.flush();
1265
1266 // With the bug present, at (55, 55) we would get a fully opaque red
1267 // intead of a dark red.
1268 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1269 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1270}
mtklein3e8232b2014-08-18 13:39:11 -07001271
1272struct CountingBBH : public SkBBoxHierarchy {
1273 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001274 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001275
schenney23d85932015-03-06 16:20:28 -08001276 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001277
mtkleinc6ad06a2015-08-19 09:51:00 -07001278 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001279 this->searchCalls++;
1280 }
1281
mtklein36352bf2015-03-25 18:17:31 -07001282 void insert(const SkRect[], int) override {}
1283 virtual size_t bytesUsed() const override { return 0; }
1284 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001285};
1286
1287class SpoonFedBBHFactory : public SkBBHFactory {
1288public:
1289 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001290 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001291 return SkRef(fBBH);
1292 }
1293private:
1294 SkBBoxHierarchy* fBBH;
1295};
1296
1297// When the canvas clip covers the full picture, we don't need to call the BBH.
1298DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001299 SkRect bound = SkRect::MakeWH(320, 240);
1300 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001301 SpoonFedBBHFactory factory(&bbh);
1302
1303 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001304 SkCanvas* c = recorder.beginRecording(bound, &factory);
1305 // Record a few ops so we don't hit a small- or empty- picture optimization.
1306 c->drawRect(bound, SkPaint());
1307 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001308 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1309
1310 SkCanvas big(640, 480), small(300, 200);
1311
robertphillipsc5ba71d2014-09-04 08:42:50 -07001312 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001313 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1314
robertphillipsc5ba71d2014-09-04 08:42:50 -07001315 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001316 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1317}
mtkleind72094d2014-08-27 12:12:23 -07001318
1319DEF_TEST(Picture_BitmapLeak, r) {
1320 SkBitmap mut, immut;
1321 mut.allocN32Pixels(300, 200);
1322 immut.allocN32Pixels(300, 200);
1323 immut.setImmutable();
1324 SkASSERT(!mut.isImmutable());
1325 SkASSERT(immut.isImmutable());
1326
1327 // No one can hold a ref on our pixels yet.
1328 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1329 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1330
reed1bdfd3f2014-11-24 14:41:51 -08001331 SkAutoTUnref<const SkPicture> pic;
1332 {
1333 // we want the recorder to go out of scope before our subsequent checks, so we
1334 // place it inside local braces.
1335 SkPictureRecorder rec;
1336 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1337 canvas->drawBitmap(mut, 0, 0);
1338 canvas->drawBitmap(immut, 800, 600);
1339 pic.reset(rec.endRecording());
1340 }
mtkleind72094d2014-08-27 12:12:23 -07001341
1342 // The picture shares the immutable pixels but copies the mutable ones.
1343 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1344 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1345
1346 // When the picture goes away, it's just our bitmaps holding the refs.
halcanary96fcdcc2015-08-27 07:41:13 -07001347 pic.reset(nullptr);
mtkleind72094d2014-08-27 12:12:23 -07001348 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1349 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1350}
mtkleinfeaadee2015-04-08 11:25:48 -07001351
1352// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1353DEF_TEST(Picture_getRecordingCanvas, r) {
1354 SkPictureRecorder rec;
1355 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1356 for (int i = 0; i < 3; i++) {
1357 rec.beginRecording(100, 100);
1358 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1359 rec.endRecording()->unref();
1360 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1361 }
1362}
mtklein9db912c2015-05-19 11:11:26 -07001363
1364DEF_TEST(MiniRecorderLeftHanging, r) {
1365 // Any shader or other ref-counted effect will do just fine here.
1366 SkPaint paint;
1367 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1368
1369 SkMiniRecorder rec;
1370 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1371 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1372}
fmalita2ecc0002015-07-14 13:12:25 -07001373
1374DEF_TEST(Picture_preserveCullRect, r) {
1375 SkPictureRecorder recorder;
1376
1377 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1378 c->clear(SK_ColorCYAN);
1379
1380 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1381 SkDynamicMemoryWStream wstream;
1382 picture->serialize(&wstream);
1383
1384 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1385 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1386
1387 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1388 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1389 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1390 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1391 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1392}