blob: 448e079958ef5a01444fc0c5a9b009c16325b27e [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"
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"
robertphillips@google.com770963f2014-04-18 18:04:41 +000023#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000024#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070025#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080026#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070027#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000028#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000029#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080030#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000031#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000032#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070033#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000034
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000035#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000036
reed@google.com47b679b2014-05-14 18:58:16 +000037#include "SkLumaColorFilter.h"
38#include "SkColorFilterImageFilter.h"
39
reed@google.comfe7b1ed2012-11-29 21:00:39 +000040static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000041 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000042 bm->eraseColor(color);
43 if (immutable) {
44 bm->setImmutable();
45 }
46}
47
mtkleina16af212015-08-26 08:14:52 -070048// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
49static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
reede3b38ce2016-01-08 09:18:44 -080050 // We just need _some_ SkImage
51 const SkPMColor pixel = 0;
52 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
53 SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(info, &pixel, sizeof(pixel)));
mtkleina16af212015-08-26 08:14:52 -070054
55 SkPictureRecorder recorder;
reede3b38ce2016-01-08 09:18:44 -080056 recorder.beginRecording(100,100)->drawImage(image, 0,0);
mtkleina16af212015-08-26 08:14:52 -070057 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
58
59 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
60}
61
tomhudson3a0f2792014-08-20 05:29:41 -070062/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070063static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070064 SkPictureRecorder recorder;
65
mtklein8e126562014-10-01 09:29:35 -070066 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070067 {
68 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
69 }
70 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
71 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
72
mtklein8e126562014-10-01 09:29:35 -070073 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070074 {
75 SkPaint paint;
76 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
77 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
78 SkBitmap bitmap;
79 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
80 bitmap.eraseColor(SK_ColorBLUE);
81 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
82 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
83 SkShader::kClamp_TileMode);
84 paint.setShader(shader)->unref();
reedf5822822015-08-19 11:46:38 -070085 REPORTER_ASSERT(reporter, shader->isABitmap());
tomhudson3a0f2792014-08-20 05:29:41 -070086
87 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
88 }
89 picture.reset(recorder.endRecording());
90 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
91}
92
93
scroggo@google.comd614c6a2012-09-14 17:26:37 +000094#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070095// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070096// in debug mode, so only run in debug mode.
97static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000098 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000099 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700100 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -0700101 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000102 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -0700103 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700104 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000105}
106
107// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
108static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000109 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700110 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000111 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000112 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000113 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000114}
115#endif
116
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000117static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000118 SkPaint paint;
119 SkRect rect = SkRect::MakeWH(50, 50);
120
121 SkScalar unit = rand.nextUScalar1();
122 if (unit <= 0.3) {
123// SkDebugf("save\n");
124 canvas->save();
125 } else if (unit <= 0.6) {
126// SkDebugf("restore\n");
127 canvas->restore();
128 } else if (unit <= 0.9) {
129// SkDebugf("clip\n");
130 canvas->clipRect(rect);
131 } else {
132// SkDebugf("draw\n");
133 canvas->drawPaint(paint);
134 }
135}
136
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000137#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700138
mtklein8e126562014-10-01 09:29:35 -0700139static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000140 SkPictureRecorder recorder;
141
mtklein8e126562014-10-01 09:29:35 -0700142 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000143 {
144 SkPath path;
145 path.moveTo(0, 0);
146 path.lineTo(50, 50);
147
148 SkScalar intervals[] = { 1.0f, 1.0f };
reed5e1ddb12015-12-21 08:52:45 -0800149 SkAutoTUnref<SkPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000150
151 SkPaint paint;
152 paint.setStyle(SkPaint::kStroke_Style);
153 paint.setPathEffect(dash);
154
robertphillips98b03152015-01-26 11:29:36 -0800155 for (int i = 0; i < 50; ++i) {
156 canvas->drawPath(path, paint);
157 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000158 }
159 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
160 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000161
halcanary96fcdcc2015-08-27 07:41:13 -0700162 const char *reason = nullptr;
163 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700164 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000165
mtklein8e126562014-10-01 09:29:35 -0700166 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000167 {
168 SkPath path;
169
170 path.moveTo(0, 0);
171 path.lineTo(0, 50);
172 path.lineTo(25, 25);
173 path.lineTo(50, 50);
174 path.lineTo(50, 0);
175 path.close();
176 REPORTER_ASSERT(reporter, !path.isConvex());
177
178 SkPaint paint;
179 paint.setAntiAlias(true);
180 for (int i = 0; i < 50; ++i) {
181 canvas->drawPath(path, paint);
182 }
183 }
184 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800185 // A lot of small AA concave paths should be fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700186 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
jvanverthd86b07a2014-11-04 08:50:15 -0800187
188 canvas = recorder.beginRecording(100, 100);
189 {
190 SkPath path;
191
192 path.moveTo(0, 0);
193 path.lineTo(0, 100);
194 path.lineTo(50, 50);
195 path.lineTo(100, 100);
196 path.lineTo(100, 0);
197 path.close();
198 REPORTER_ASSERT(reporter, !path.isConvex());
199
200 SkPaint paint;
201 paint.setAntiAlias(true);
202 for (int i = 0; i < 50; ++i) {
203 canvas->drawPath(path, paint);
204 }
205 }
206 picture.reset(recorder.endRecording());
207 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700208 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000209
mtklein8e126562014-10-01 09:29:35 -0700210 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000211 {
212 SkPath path;
213
214 path.moveTo(0, 0);
215 path.lineTo(0, 50);
216 path.lineTo(25, 25);
217 path.lineTo(50, 50);
218 path.lineTo(50, 0);
219 path.close();
220 REPORTER_ASSERT(reporter, !path.isConvex());
221
222 SkPaint paint;
223 paint.setAntiAlias(true);
224 paint.setStyle(SkPaint::kStroke_Style);
225 paint.setStrokeWidth(0);
226 for (int i = 0; i < 50; ++i) {
227 canvas->drawPath(path, paint);
228 }
229 }
230 picture.reset(recorder.endRecording());
231 // hairline stroked AA concave paths are fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700232 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700233
mtklein8e126562014-10-01 09:29:35 -0700234 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700235 {
236 SkPaint paint;
237 SkScalar intervals [] = { 10, 20 };
238 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
239 paint.setPathEffect(pe)->unref();
240
241 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800242
243 for (int i = 0; i < 50; ++i) {
244 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
245 }
tomhudson3a0f2792014-08-20 05:29:41 -0700246 }
247 picture.reset(recorder.endRecording());
248 // fast-path dashed effects are fine for GPU rendering ...
halcanary96fcdcc2015-08-27 07:41:13 -0700249 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700250
mtklein8e126562014-10-01 09:29:35 -0700251 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700252 {
253 SkPaint paint;
254 SkScalar intervals [] = { 10, 20 };
255 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
256 paint.setPathEffect(pe)->unref();
257
robertphillips98b03152015-01-26 11:29:36 -0800258 for (int i = 0; i < 50; ++i) {
259 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
260 }
tomhudson3a0f2792014-08-20 05:29:41 -0700261 }
262 picture.reset(recorder.endRecording());
263 // ... but only when applied to drawPoint() calls
halcanary96fcdcc2015-08-27 07:41:13 -0700264 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
mtklein53fecfb2014-08-21 09:11:37 -0700265
266 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700267 canvas = recorder.beginRecording(100, 100);
268 {
269 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700270 }
mtklein8e126562014-10-01 09:29:35 -0700271 picture.reset(recorder.endRecording());
halcanary96fcdcc2015-08-27 07:41:13 -0700272 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000273}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000274
robertphillips82365912014-11-12 09:32:34 -0800275#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000276
robertphillips82365912014-11-12 09:32:34 -0800277static void test_savelayer_extraction(skiatest::Reporter* reporter) {
278 static const int kWidth = 100;
279 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000280
robertphillips82365912014-11-12 09:32:34 -0800281 // Create complex paint that the bounding box computation code can't
282 // optimize away
283 SkScalar blueToRedMatrix[20] = { 0 };
284 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
285 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
286 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000287
robertphillips82365912014-11-12 09:32:34 -0800288 SkPaint complexPaint;
289 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000290
robertphillips82365912014-11-12 09:32:34 -0800291 SkAutoTUnref<SkPicture> pict, child;
292 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000293
robertphillips82365912014-11-12 09:32:34 -0800294 {
295 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700296
robertphillips82365912014-11-12 09:32:34 -0800297 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
298 &bbhFactory,
299 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700300
halcanary96fcdcc2015-08-27 07:41:13 -0700301 c->saveLayer(nullptr, &complexPaint);
robertphillips82365912014-11-12 09:32:34 -0800302 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700303
robertphillips82365912014-11-12 09:32:34 -0800304 child.reset(recorder.endRecording());
305 }
robertphillipsd6283302014-08-27 11:53:28 -0700306
robertphillips82365912014-11-12 09:32:34 -0800307 // create a picture with the structure:
308 // 1)
309 // SaveLayer
310 // Restore
311 // 2)
312 // SaveLayer
313 // Translate
314 // SaveLayer w/ bound
315 // Restore
316 // Restore
317 // 3)
318 // SaveLayer w/ copyable paint
319 // Restore
320 // 4)
321 // SaveLayer
322 // DrawPicture (which has a SaveLayer/Restore pair)
323 // Restore
324 // 5)
325 // SaveLayer
326 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
327 // Restore
328 {
329 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700330
robertphillips82365912014-11-12 09:32:34 -0800331 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
332 SkIntToScalar(kHeight),
333 &bbhFactory,
334 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000335 // 1)
halcanary96fcdcc2015-08-27 07:41:13 -0700336 c->saveLayer(nullptr, &complexPaint); // layer #0
robertphillips82365912014-11-12 09:32:34 -0800337 c->restore();
338
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000339 // 2)
halcanary96fcdcc2015-08-27 07:41:13 -0700340 c->saveLayer(nullptr, nullptr); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800341 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800342 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
343 c->saveLayer(&r, &complexPaint); // layer #2
344 c->restore();
345 c->restore();
346
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000347 // 3)
robertphillips82365912014-11-12 09:32:34 -0800348 {
halcanary96fcdcc2015-08-27 07:41:13 -0700349 c->saveLayer(nullptr, &complexPaint); // layer #3
robertphillips82365912014-11-12 09:32:34 -0800350 c->restore();
351 }
352
353 SkPaint layerPaint;
354 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000355 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000356 {
halcanary96fcdcc2015-08-27 07:41:13 -0700357 c->saveLayer(nullptr, &layerPaint); // layer #4
robertphillips82365912014-11-12 09:32:34 -0800358 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000359 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800360 }
361 // 5
362 {
363 SkPaint picturePaint;
364 SkMatrix trans;
365 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700366
halcanary96fcdcc2015-08-27 07:41:13 -0700367 c->saveLayer(nullptr, &layerPaint); // layer #6
robertphillips82365912014-11-12 09:32:34 -0800368 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700369 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000370 }
371
robertphillips82365912014-11-12 09:32:34 -0800372 pict.reset(recorder.endRecording());
373 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000374
robertphillips82365912014-11-12 09:32:34 -0800375 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800376 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
mtklein9db912c2015-05-19 11:11:26 -0700377 const SkBigPicture* bp = pict->asSkBigPicture();
378 REPORTER_ASSERT(reporter, bp);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000379
mtklein9db912c2015-05-19 11:11:26 -0700380 const SkBigPicture::AccelData* data = bp->accelData();
robertphillips82365912014-11-12 09:32:34 -0800381 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000382
robertphillips82365912014-11-12 09:32:34 -0800383 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
384 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700385
robertphillips82365912014-11-12 09:32:34 -0800386 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
387 // The parent/child layers appear in reverse order
388 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
389 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700390
robertphillips82365912014-11-12 09:32:34 -0800391 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700392
robertphillips82365912014-11-12 09:32:34 -0800393 // The parent/child layers appear in reverse order
394 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
395 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000396
robertphillips82365912014-11-12 09:32:34 -0800397 // The parent/child layers appear in reverse order
398 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
399 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000400
halcanary96fcdcc2015-08-27 07:41:13 -0700401 REPORTER_ASSERT(reporter, nullptr == info0.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800402 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
403 kHeight == info0.fBounds.height());
404 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
405 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
406 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700407 REPORTER_ASSERT(reporter, nullptr != info0.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800408 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000409
halcanary96fcdcc2015-08-27 07:41:13 -0700410 REPORTER_ASSERT(reporter, nullptr == info1.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800411 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
412 kHeight/2.0 == info1.fBounds.height());
413 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
414 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800415 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800416 kHeight/2.0 == info1.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700417 REPORTER_ASSERT(reporter, nullptr == info1.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800418 REPORTER_ASSERT(reporter, !info1.fIsNested &&
419 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000420
halcanary96fcdcc2015-08-27 07:41:13 -0700421 REPORTER_ASSERT(reporter, nullptr == info2.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800422 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
423 kHeight / 2 == info2.fBounds.height()); // bound reduces size
424 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
425 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
426 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
427 kHeight / 2 == info2.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700428 REPORTER_ASSERT(reporter, nullptr != info2.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800429 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000430
halcanary96fcdcc2015-08-27 07:41:13 -0700431 REPORTER_ASSERT(reporter, nullptr == info3.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800432 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
433 kHeight == info3.fBounds.height());
434 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
435 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
436 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
437 REPORTER_ASSERT(reporter, info3.fPaint);
438 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700439
halcanary96fcdcc2015-08-27 07:41:13 -0700440 REPORTER_ASSERT(reporter, nullptr == info4.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800441 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
442 kHeight == info4.fBounds.height());
443 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
444 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
445 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
446 REPORTER_ASSERT(reporter, info4.fPaint);
447 REPORTER_ASSERT(reporter, !info4.fIsNested &&
448 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700449
robertphillips82365912014-11-12 09:32:34 -0800450 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
451 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
452 kHeight == info5.fBounds.height());
453 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
454 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
455 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700456 REPORTER_ASSERT(reporter, nullptr != info5.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800457 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700458
halcanary96fcdcc2015-08-27 07:41:13 -0700459 REPORTER_ASSERT(reporter, nullptr == info6.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800460 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
461 kHeight-10 == info6.fBounds.height());
462 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
463 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
464 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
465 REPORTER_ASSERT(reporter, info6.fPaint);
466 REPORTER_ASSERT(reporter, !info6.fIsNested &&
467 info6.fHasNestedLayers); // has a nested SL
468
469 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
470 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
471 kHeight == info7.fBounds.height());
472 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
473 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
474 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700475 REPORTER_ASSERT(reporter, nullptr != info7.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800476 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000477 }
478}
479
mtklein8e126562014-10-01 09:29:35 -0700480static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700481 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700482
mtklein8e126562014-10-01 09:29:35 -0700483 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700484 {
mtkleinc551d9f2014-08-20 08:09:46 -0700485 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700486 }
487 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
488 REPORTER_ASSERT(reporter, !picture->hasText());
489
mtkleinc551d9f2014-08-20 08:09:46 -0700490 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700491 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700492 {
mtkleinc551d9f2014-08-20 08:09:46 -0700493 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700494 }
495 picture.reset(recorder.endRecording());
496 REPORTER_ASSERT(reporter, picture->hasText());
497
mtklein8e126562014-10-01 09:29:35 -0700498 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700499 {
mtkleinc551d9f2014-08-20 08:09:46 -0700500 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700501 }
502 picture.reset(recorder.endRecording());
503 REPORTER_ASSERT(reporter, picture->hasText());
504
mtklein8e126562014-10-01 09:29:35 -0700505 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700506 {
mtkleinc551d9f2014-08-20 08:09:46 -0700507 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700508 }
509 picture.reset(recorder.endRecording());
510 REPORTER_ASSERT(reporter, picture->hasText());
511
mtklein8e126562014-10-01 09:29:35 -0700512 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700513 {
514 SkPath path;
515 path.moveTo(0, 0);
516 path.lineTo(50, 50);
517
mtkleinc551d9f2014-08-20 08:09:46 -0700518 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700519 }
520 picture.reset(recorder.endRecording());
521 REPORTER_ASSERT(reporter, picture->hasText());
522
mtklein8e126562014-10-01 09:29:35 -0700523 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700524 {
525 SkPath path;
526 path.moveTo(0, 0);
527 path.lineTo(50, 50);
528
halcanary96fcdcc2015-08-27 07:41:13 -0700529 canvas->drawTextOnPath("Q", 1, path, nullptr, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700530 }
531 picture.reset(recorder.endRecording());
532 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700533
534 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700535 canvas = recorder.beginRecording(100,100);
536 {
537 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700538 }
mtklein8e126562014-10-01 09:29:35 -0700539 picture.reset(recorder.endRecording());
540 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700541}
542
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000543static void set_canvas_to_save_count_4(SkCanvas* canvas) {
544 canvas->restoreToCount(1);
545 canvas->save();
546 canvas->save();
547 canvas->save();
548}
549
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000550/**
551 * A canvas that records the number of saves, saveLayers and restores.
552 */
553class SaveCountingCanvas : public SkCanvas {
554public:
555 SaveCountingCanvas(int width, int height)
556 : INHERITED(width, height)
557 , fSaveCount(0)
558 , fSaveLayerCount(0)
559 , fRestoreCount(0){
560 }
561
reed4960eee2015-12-18 07:09:18 -0800562 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000563 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800564 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000565 }
566
mtklein36352bf2015-03-25 18:17:31 -0700567 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000568 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400569 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000570 }
571
mtklein36352bf2015-03-25 18:17:31 -0700572 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000573 ++fRestoreCount;
574 this->INHERITED::willRestore();
575 }
576
577 unsigned int getSaveCount() const { return fSaveCount; }
578 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
579 unsigned int getRestoreCount() const { return fRestoreCount; }
580
581private:
582 unsigned int fSaveCount;
583 unsigned int fSaveLayerCount;
584 unsigned int fRestoreCount;
585
586 typedef SkCanvas INHERITED;
587};
588
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000589void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000590 unsigned int numSaves, unsigned int numSaveLayers,
591 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700592 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700593 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000594
robertphillipsc5ba71d2014-09-04 08:42:50 -0700595 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000596
mtklein87c41382014-09-08 07:31:18 -0700597 // Optimizations may have removed these,
598 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
599 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
600 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
601 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000602}
603
604// This class exists so SkPicture can friend it and give it access to
605// the 'partialReplay' method.
606class SkPictureRecorderReplayTester {
607public:
608 static SkPicture* Copy(SkPictureRecorder* recorder) {
609 SkPictureRecorder recorder2;
610
robertphillips9f1c2412014-06-09 06:25:34 -0700611 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000612
613 recorder->partialReplay(canvas);
614
615 return recorder2.endRecording();
616 }
617};
618
robertphillips9058d602014-06-10 11:45:46 -0700619static void create_imbalance(SkCanvas* canvas) {
620 SkRect clipRect = SkRect::MakeWH(2, 2);
621 SkRect drawRect = SkRect::MakeWH(10, 10);
622 canvas->save();
623 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
624 canvas->translate(1.0f, 1.0f);
625 SkPaint p;
626 p.setColor(SK_ColorGREEN);
627 canvas->drawRect(drawRect, p);
628 // no restore
629}
630
631// This tests that replaying a potentially unbalanced picture into a canvas
632// doesn't affect the canvas' save count or matrix/clip state.
633static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
634 SkBitmap bm;
635 bm.allocN32Pixels(4, 3);
636 SkCanvas canvas(bm);
637
638 int beforeSaveCount = canvas.getSaveCount();
639
640 SkMatrix beforeMatrix = canvas.getTotalMatrix();
641
642 SkRect beforeClip;
643
644 canvas.getClipBounds(&beforeClip);
645
646 canvas.drawPicture(picture);
647
648 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
649 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
650
651 SkRect afterClip;
652
653 canvas.getClipBounds(&afterClip);
654
655 REPORTER_ASSERT(reporter, afterClip == beforeClip);
656}
657
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000658// Test out SkPictureRecorder::partialReplay
659DEF_TEST(PictureRecorder_replay, reporter) {
660 // check save/saveLayer state
661 {
662 SkPictureRecorder recorder;
663
robertphillips9f1c2412014-06-09 06:25:34 -0700664 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000665
halcanary96fcdcc2015-08-27 07:41:13 -0700666 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000667
668 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
669
670 // The extra save and restore comes from the Copy process.
671 check_save_state(reporter, copy, 2, 1, 3);
672
halcanary96fcdcc2015-08-27 07:41:13 -0700673 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000674
675 SkAutoTUnref<SkPicture> final(recorder.endRecording());
676
677 check_save_state(reporter, final, 1, 2, 3);
678
679 // The copy shouldn't pick up any operations added after it was made
680 check_save_state(reporter, copy, 2, 1, 3);
681 }
682
683 // (partially) check leakage of draw ops
684 {
685 SkPictureRecorder recorder;
686
robertphillips9f1c2412014-06-09 06:25:34 -0700687 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000688
689 SkRect r = SkRect::MakeWH(5, 5);
690 SkPaint p;
691
692 canvas->drawRect(r, p);
693
694 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
695
696 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
697
698 SkBitmap bm;
699 make_bm(&bm, 10, 10, SK_ColorRED, true);
700
701 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700702 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000703
704 SkAutoTUnref<SkPicture> final(recorder.endRecording());
705 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
706
707 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
708
709 // The snapshot shouldn't pick up any operations added after it was made
710 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
711 }
robertphillips9058d602014-06-10 11:45:46 -0700712
713 // Recreate the Android partialReplay test case
714 {
715 SkPictureRecorder recorder;
716
halcanary96fcdcc2015-08-27 07:41:13 -0700717 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700718 create_imbalance(canvas);
719
720 int expectedSaveCount = canvas->getSaveCount();
721
722 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
723 check_balance(reporter, copy);
724
725 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
726
727 // End the recording of source to test the picture finalization
728 // process isn't complicated by the partialReplay step
729 SkAutoTUnref<SkPicture> final(recorder.endRecording());
730 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000731}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000732
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000733static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
734 SkCanvas testCanvas(100, 100);
735 set_canvas_to_save_count_4(&testCanvas);
736
737 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
738
739 SkPaint paint;
740 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
741
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000742 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000743
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000744 {
745 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700746 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000747 canvas->save();
748 canvas->translate(10, 10);
749 canvas->drawRect(rect, paint);
750 canvas->save();
751 canvas->translate(10, 10);
752 canvas->drawRect(rect, paint);
753 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
754
robertphillips9b14f262014-06-04 05:40:44 -0700755 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000756 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
757 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000758
759 set_canvas_to_save_count_4(&testCanvas);
760
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000761 {
762 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700763 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000764 canvas->save();
765 canvas->translate(10, 10);
766 canvas->drawRect(rect, paint);
767 canvas->save();
768 canvas->translate(10, 10);
769 canvas->drawRect(rect, paint);
770 canvas->restore();
771 canvas->restore();
772 canvas->restore();
773 canvas->restore();
774 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000775
robertphillips9b14f262014-06-04 05:40:44 -0700776 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000777 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
778 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000779
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000780 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000781
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000782 {
robertphillips9f1c2412014-06-09 06:25:34 -0700783 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000784 canvas->translate(10, 10);
785 canvas->drawRect(rect, paint);
786 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
787
robertphillips9b14f262014-06-04 05:40:44 -0700788 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000789 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
790 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
791 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000792}
793
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000794static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000795 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000796
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000797 SkPictureRecorder recorder;
798
reed@google.com21b519d2012-10-02 17:42:15 +0000799 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000800 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000801
robertphillips9f1c2412014-06-09 06:25:34 -0700802 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000803
804 for (int i = 0; i < 1000; ++i) {
805 rand_op(canvas, rand);
806 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000807 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000808
809 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000810 }
811
812 {
robertphillips9f1c2412014-06-09 06:25:34 -0700813 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000814 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000815
reed@google.com21b519d2012-10-02 17:42:15 +0000816 for (int i = 0; i < 100; ++i) {
817 canvas->save();
818 }
819 while (canvas->getSaveCount() > 1) {
820 canvas->clipRect(rect);
821 canvas->restore();
822 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000823 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000824 }
825}
826
scroggo@google.com4b90b112012-12-04 15:08:56 +0000827#ifndef SK_DEBUG
828// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
829// should never do this.
830static void test_bad_bitmap() {
831 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
832 // fail.
833 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000834 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000835 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700836 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000837 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000838 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000839
840 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700841 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000842}
843#endif
844
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000845static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000846 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700847 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700848 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000849 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000850 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
851
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000852 SkDynamicMemoryWStream wStream;
halcanaryf2848b62015-12-10 12:40:23 -0800853 SkAutoTUnref<SkPixelSerializer> serializer(
854 SkImageEncoder::CreatePixelSerializer());
855 picture->serialize(&wStream, serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000856 return wStream.copyToData();
857}
858
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000859struct ErrorContext {
860 int fErrors;
861 skiatest::Reporter* fReporter;
862};
863
864static void assert_one_parse_error_cb(SkError error, void* context) {
865 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
866 errorContext->fErrors++;
867 // This test only expects one error, and that is a kParseError. If there are others,
868 // there is some unknown problem.
869 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
870 "This threw more errors than expected.");
871 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
872 SkGetLastErrorString());
873}
874
msarett8715d472016-02-17 10:02:29 -0800875static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
876 SkAutoLockPixels autoLockPixels(bm);
877 SkASSERT(bm.getPixels());
878 SkMD5 md5;
879 size_t rowLen = bm.info().bytesPerPixel() * bm.width();
880 for (int y = 0; y < bm.height(); ++y) {
881 md5.update(static_cast<uint8_t*>(bm.getAddr(0, y)), rowLen);
882 }
883 md5.finish(*digest);
884}
885
886DEF_TEST(Picture_EncodedData, reporter) {
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000887 // Create a bitmap that will be encoded.
888 SkBitmap original;
889 make_bm(&original, 100, 100, SK_ColorBLUE, true);
890 SkDynamicMemoryWStream wStream;
891 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
892 return;
893 }
894 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000895
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000896 SkBitmap bm;
reedd1146452015-09-25 06:56:57 -0700897 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000898 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000899
900 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
901 // Flattening original will follow the old path of performing an encode, while flattening bm
902 // will use the already encoded data.
903 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
904 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
905 REPORTER_ASSERT(reporter, picture1->equals(picture2));
msarett8715d472016-02-17 10:02:29 -0800906
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000907 // Now test that a parse error was generated when trying to create a new SkPicture without
908 // providing a function to decode the bitmap.
909 ErrorContext context;
910 context.fErrors = 0;
911 context.fReporter = reporter;
912 SkSetErrorCallback(assert_one_parse_error_cb, &context);
913 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000914 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700915 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, nullptr));
916 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000917 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700918 SkSetErrorCallback(nullptr, nullptr);
msarett8715d472016-02-17 10:02:29 -0800919
920 // Test that using the version of CreateFromStream that just takes a stream also decodes the
921 // bitmap. Drawing this picture should look exactly like the original bitmap.
922 SkMD5::Digest referenceDigest;
923 md5(original, &referenceDigest);
924
925 SkBitmap dst;
926 dst.allocPixels(original.info());
927 dst.eraseColor(SK_ColorRED);
928 SkCanvas canvas(dst);
929
930 pictureStream.rewind();
931 pictureFromStream.reset(SkPicture::CreateFromStream(&pictureStream));
932 canvas.drawPicture(pictureFromStream.get());
933
934 SkMD5::Digest digest2;
935 md5(dst, &digest2);
936 REPORTER_ASSERT(reporter, referenceDigest == digest2);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000937}
938
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000939static void test_clip_bound_opt(skiatest::Reporter* reporter) {
940 // Test for crbug.com/229011
941 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
942 SkIntToScalar(2), SkIntToScalar(2));
943 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
944 SkIntToScalar(1), SkIntToScalar(1));
945 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
946 SkIntToScalar(1), SkIntToScalar(1));
947
948 SkPath invPath;
949 invPath.addOval(rect1);
950 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
951 SkPath path;
952 path.addOval(rect2);
953 SkPath path2;
954 path2.addOval(rect3);
955 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000956 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700957
958 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000959 {
robertphillips9f1c2412014-06-09 06:25:34 -0700960 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000961 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
962 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
963 REPORTER_ASSERT(reporter, true == nonEmpty);
964 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
965 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
966 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
967 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
968 }
969 {
robertphillips9f1c2412014-06-09 06:25:34 -0700970 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000971 canvas->clipPath(path, SkRegion::kIntersect_Op);
972 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
973 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
974 REPORTER_ASSERT(reporter, true == nonEmpty);
975 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
976 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
977 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
978 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
979 }
980 {
robertphillips9f1c2412014-06-09 06:25:34 -0700981 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000982 canvas->clipPath(path, SkRegion::kIntersect_Op);
983 canvas->clipPath(invPath, SkRegion::kUnion_Op);
984 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
985 REPORTER_ASSERT(reporter, true == nonEmpty);
986 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
987 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
988 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
989 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
990 }
991 {
robertphillips9f1c2412014-06-09 06:25:34 -0700992 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000993 canvas->clipPath(path, SkRegion::kDifference_Op);
994 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
995 REPORTER_ASSERT(reporter, true == nonEmpty);
996 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
997 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
998 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
999 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
1000 }
1001 {
robertphillips9f1c2412014-06-09 06:25:34 -07001002 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001003 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
1004 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
1005 // True clip is actually empty in this case, but the best
1006 // determination we can make using only bounds as input is that the
1007 // clip is included in the bounds of 'path'.
1008 REPORTER_ASSERT(reporter, true == nonEmpty);
1009 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
1010 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
1011 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
1012 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
1013 }
1014 {
robertphillips9f1c2412014-06-09 06:25:34 -07001015 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001016 canvas->clipPath(path, SkRegion::kIntersect_Op);
1017 canvas->clipPath(path2, SkRegion::kXOR_Op);
1018 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
1019 REPORTER_ASSERT(reporter, true == nonEmpty);
1020 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
1021 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
1022 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
1023 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
1024 }
1025}
1026
schenneyeeff8bb2015-07-07 14:27:10 -07001027static void test_cull_rect_reset(skiatest::Reporter* reporter) {
1028 SkPictureRecorder recorder;
1029 SkRect bounds = SkRect::MakeWH(10, 10);
1030 SkRTreeFactory factory;
1031 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
1032 bounds = SkRect::MakeWH(100, 100);
1033 SkPaint paint;
1034 canvas->drawRect(bounds, paint);
1035 canvas->drawRect(bounds, paint);
mtkleineedc3342015-07-08 08:26:39 -07001036 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
1037 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -07001038 REPORTER_ASSERT(reporter, picture);
1039
1040 SkRect finalCullRect = picture->cullRect();
1041 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1042 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1043 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1044 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1045
1046 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1047 SkRect bbhCullRect = pictureBBH->getRootBound();
1048 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1049 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1050 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1051 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1052}
1053
1054
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001055/**
1056 * A canvas that records the number of clip commands.
1057 */
1058class ClipCountingCanvas : public SkCanvas {
1059public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001060 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001061 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001062 , fClipCount(0){
1063 }
1064
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001065 virtual void onClipRect(const SkRect& r,
1066 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001067 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001068 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001069 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001070 }
1071
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001072 virtual void onClipRRect(const SkRRect& rrect,
1073 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001074 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001075 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001076 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001077 }
1078
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001079 virtual void onClipPath(const SkPath& path,
1080 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001081 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001082 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001083 this->INHERITED::onClipPath(path, op, edgeStyle);
1084 }
1085
mtklein36352bf2015-03-25 18:17:31 -07001086 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001087 fClipCount += 1;
1088 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001089 }
1090
1091 unsigned getClipCount() const { return fClipCount; }
1092
1093private:
1094 unsigned fClipCount;
1095
1096 typedef SkCanvas INHERITED;
1097};
1098
1099static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001100 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001101 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001102
1103 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1104 // The following expanding clip should not be skipped.
1105 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1106 // Draw something so the optimizer doesn't just fold the world.
1107 SkPaint p;
1108 p.setColor(SK_ColorBLUE);
1109 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001110 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001111
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001112 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001113 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001114
1115 // Both clips should be present on playback.
1116 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1117}
1118
tomhudson@google.com381010e2013-10-24 11:12:47 +00001119static void test_hierarchical(skiatest::Reporter* reporter) {
1120 SkBitmap bm;
1121 make_bm(&bm, 10, 10, SK_ColorRED, true);
1122
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001123 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001124
robertphillips9f1c2412014-06-09 06:25:34 -07001125 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001126 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1127 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001128
robertphillips9f1c2412014-06-09 06:25:34 -07001129 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001130 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1131 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001132
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001133 {
robertphillips9f1c2412014-06-09 06:25:34 -07001134 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001135 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001136 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1137 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1138 }
1139 {
robertphillips9f1c2412014-06-09 06:25:34 -07001140 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001141 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001142 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1143 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1144 }
1145 {
robertphillips9f1c2412014-06-09 06:25:34 -07001146 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001147 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001148 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001149 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1150 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1151 }
1152 {
robertphillips9f1c2412014-06-09 06:25:34 -07001153 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001154 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001155 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001156 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1157 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1158 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001159}
1160
robertphillips@google.comd5500882014-04-02 23:51:13 +00001161static void test_gen_id(skiatest::Reporter* reporter) {
1162
Robert Phillipscfaeec42014-07-13 12:00:50 -04001163 SkPictureRecorder recorder;
1164 recorder.beginRecording(0, 0);
1165 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001166
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001167 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001168 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001169
robertphillips9f1c2412014-06-09 06:25:34 -07001170 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001171 canvas->drawARGB(255, 255, 255, 255);
1172 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1173 // picture should have a non-zero id after recording
1174 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001175
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001176 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001177 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001178}
1179
caryclark5ef194c2015-08-31 09:22:38 -07001180static void test_typeface(skiatest::Reporter* reporter) {
1181 SkPictureRecorder recorder;
1182 SkCanvas* canvas = recorder.beginRecording(10, 10);
1183 SkPaint paint;
1184 paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic));
1185 canvas->drawText("Q", 1, 0, 10, paint);
1186 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1187 REPORTER_ASSERT(reporter, picture->hasText());
1188 SkDynamicMemoryWStream stream;
1189 picture->serialize(&stream);
1190}
1191
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001192DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -07001193 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001194#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001195 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001196 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001197#else
1198 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001199#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001200 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001201 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001202#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001203 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001204#endif
mtklein8e126562014-10-01 09:29:35 -07001205 test_has_text(reporter);
mtkleina16af212015-08-26 08:14:52 -07001206 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -07001207 test_analysis(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001208 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001209 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001210 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001211 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001212 test_savelayer_extraction(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -07001213 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001214}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001215
1216static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1217 const SkPaint paint;
1218 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1219 const SkIRect irect = { 2, 2, 3, 3 };
1220
1221 // Don't care what these record, as long as they're legal.
1222 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001223 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001224 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -08001225 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001226}
1227
1228static void test_draw_bitmaps(SkCanvas* canvas) {
1229 SkBitmap empty;
1230 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001231 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001232 draw_bitmaps(empty, canvas);
1233}
1234
1235DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001236 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001237 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001238 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001239}
1240
1241DEF_TEST(Canvas_EmptyBitmap, r) {
1242 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001243 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001244 SkCanvas canvas(dst);
1245
1246 test_draw_bitmaps(&canvas);
1247}
dneto3f22e8c2014-07-30 15:42:22 -07001248
1249DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1250 // This test is from crbug.com/344987.
1251 // The commands are:
1252 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001253 // drawBitmapRect
1254 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001255 // restore
1256 // The bug was that this structure was modified so that:
1257 // - The saveLayer and restore were eliminated
1258 // - The alpha was only applied to the first drawBitmapRectToRect
1259
1260 // This test draws blue and red squares inside a 50% transparent
1261 // layer. Both colours should show up muted.
1262 // When the bug is present, the red square (the second bitmap)
1263 // shows upwith full opacity.
1264
1265 SkBitmap blueBM;
1266 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1267 SkBitmap redBM;
1268 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1269 SkPaint semiTransparent;
1270 semiTransparent.setAlpha(0x80);
1271
1272 SkPictureRecorder recorder;
1273 SkCanvas* canvas = recorder.beginRecording(100, 100);
1274 canvas->drawARGB(0, 0, 0, 0);
1275
1276 canvas->saveLayer(0, &semiTransparent);
1277 canvas->drawBitmap(blueBM, 25, 25);
1278 canvas->drawBitmap(redBM, 50, 50);
1279 canvas->restore();
1280
1281 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1282
1283 // Now replay the picture back on another canvas
1284 // and check a couple of its pixels.
1285 SkBitmap replayBM;
1286 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1287 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001288 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001289 replayCanvas.flush();
1290
1291 // With the bug present, at (55, 55) we would get a fully opaque red
1292 // intead of a dark red.
1293 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1294 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1295}
mtklein3e8232b2014-08-18 13:39:11 -07001296
1297struct CountingBBH : public SkBBoxHierarchy {
1298 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001299 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001300
schenney23d85932015-03-06 16:20:28 -08001301 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001302
mtkleinc6ad06a2015-08-19 09:51:00 -07001303 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001304 this->searchCalls++;
1305 }
1306
mtklein36352bf2015-03-25 18:17:31 -07001307 void insert(const SkRect[], int) override {}
1308 virtual size_t bytesUsed() const override { return 0; }
1309 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001310};
1311
1312class SpoonFedBBHFactory : public SkBBHFactory {
1313public:
1314 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001315 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001316 return SkRef(fBBH);
1317 }
1318private:
1319 SkBBoxHierarchy* fBBH;
1320};
1321
1322// When the canvas clip covers the full picture, we don't need to call the BBH.
1323DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001324 SkRect bound = SkRect::MakeWH(320, 240);
1325 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001326 SpoonFedBBHFactory factory(&bbh);
1327
1328 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001329 SkCanvas* c = recorder.beginRecording(bound, &factory);
1330 // Record a few ops so we don't hit a small- or empty- picture optimization.
1331 c->drawRect(bound, SkPaint());
1332 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001333 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1334
1335 SkCanvas big(640, 480), small(300, 200);
1336
robertphillipsc5ba71d2014-09-04 08:42:50 -07001337 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001338 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1339
robertphillipsc5ba71d2014-09-04 08:42:50 -07001340 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001341 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1342}
mtkleind72094d2014-08-27 12:12:23 -07001343
1344DEF_TEST(Picture_BitmapLeak, r) {
1345 SkBitmap mut, immut;
1346 mut.allocN32Pixels(300, 200);
1347 immut.allocN32Pixels(300, 200);
1348 immut.setImmutable();
1349 SkASSERT(!mut.isImmutable());
1350 SkASSERT(immut.isImmutable());
1351
1352 // No one can hold a ref on our pixels yet.
1353 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1354 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1355
reed1bdfd3f2014-11-24 14:41:51 -08001356 SkAutoTUnref<const SkPicture> pic;
1357 {
1358 // we want the recorder to go out of scope before our subsequent checks, so we
1359 // place it inside local braces.
1360 SkPictureRecorder rec;
1361 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1362 canvas->drawBitmap(mut, 0, 0);
1363 canvas->drawBitmap(immut, 800, 600);
1364 pic.reset(rec.endRecording());
1365 }
mtkleind72094d2014-08-27 12:12:23 -07001366
1367 // The picture shares the immutable pixels but copies the mutable ones.
1368 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1369 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1370
1371 // When the picture goes away, it's just our bitmaps holding the refs.
halcanary96fcdcc2015-08-27 07:41:13 -07001372 pic.reset(nullptr);
mtkleind72094d2014-08-27 12:12:23 -07001373 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1374 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1375}
mtkleinfeaadee2015-04-08 11:25:48 -07001376
1377// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1378DEF_TEST(Picture_getRecordingCanvas, r) {
1379 SkPictureRecorder rec;
1380 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1381 for (int i = 0; i < 3; i++) {
1382 rec.beginRecording(100, 100);
1383 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1384 rec.endRecording()->unref();
1385 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1386 }
1387}
mtklein9db912c2015-05-19 11:11:26 -07001388
1389DEF_TEST(MiniRecorderLeftHanging, r) {
1390 // Any shader or other ref-counted effect will do just fine here.
1391 SkPaint paint;
1392 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1393
1394 SkMiniRecorder rec;
1395 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1396 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1397}
fmalita2ecc0002015-07-14 13:12:25 -07001398
1399DEF_TEST(Picture_preserveCullRect, r) {
1400 SkPictureRecorder recorder;
1401
1402 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1403 c->clear(SK_ColorCYAN);
1404
1405 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1406 SkDynamicMemoryWStream wstream;
1407 picture->serialize(&wstream);
1408
1409 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1410 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1411
1412 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1413 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1414 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1415 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1416 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1417}