blob: fdb2de494846fcb4f2ba1de5b4079b7c6635c984 [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
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000034#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000035
reed@google.com47b679b2014-05-14 18:58:16 +000036#include "SkLumaColorFilter.h"
37#include "SkColorFilterImageFilter.h"
38
reed@google.comfe7b1ed2012-11-29 21:00:39 +000039static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000040 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000041 bm->eraseColor(color);
42 if (immutable) {
43 bm->setImmutable();
44 }
45}
46
mtkleina16af212015-08-26 08:14:52 -070047// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
48static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
49 // We just need _some_ SkImage.
50 SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(SkBitmap()));
51
52 SkPictureRecorder recorder;
53 {
54 auto canvas = recorder.beginRecording(100,100);
55 canvas->drawImage(image, 0,0);
56 }
57 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 };
reed2d6ba662015-12-20 20:08:42 -0800149 SkAutoTUnref<SkDashPathEffect> 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
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000875static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
876 // Create a bitmap that will be encoded.
877 SkBitmap original;
878 make_bm(&original, 100, 100, SK_ColorBLUE, true);
879 SkDynamicMemoryWStream wStream;
880 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
881 return;
882 }
883 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000884
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000885 SkBitmap bm;
reedd1146452015-09-25 06:56:57 -0700886 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000887 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000888
889 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
890 // Flattening original will follow the old path of performing an encode, while flattening bm
891 // will use the already encoded data.
892 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
893 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
894 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000895 // Now test that a parse error was generated when trying to create a new SkPicture without
896 // providing a function to decode the bitmap.
897 ErrorContext context;
898 context.fErrors = 0;
899 context.fReporter = reporter;
900 SkSetErrorCallback(assert_one_parse_error_cb, &context);
901 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000902 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700903 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, nullptr));
904 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000905 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700906 SkSetErrorCallback(nullptr, nullptr);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000907}
908
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000909static void test_clip_bound_opt(skiatest::Reporter* reporter) {
910 // Test for crbug.com/229011
911 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
912 SkIntToScalar(2), SkIntToScalar(2));
913 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
914 SkIntToScalar(1), SkIntToScalar(1));
915 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
916 SkIntToScalar(1), SkIntToScalar(1));
917
918 SkPath invPath;
919 invPath.addOval(rect1);
920 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
921 SkPath path;
922 path.addOval(rect2);
923 SkPath path2;
924 path2.addOval(rect3);
925 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000926 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700927
928 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000929 {
robertphillips9f1c2412014-06-09 06:25:34 -0700930 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000931 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
932 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
933 REPORTER_ASSERT(reporter, true == nonEmpty);
934 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
935 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
936 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
937 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
938 }
939 {
robertphillips9f1c2412014-06-09 06:25:34 -0700940 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000941 canvas->clipPath(path, SkRegion::kIntersect_Op);
942 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
943 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
944 REPORTER_ASSERT(reporter, true == nonEmpty);
945 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
946 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
947 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
948 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
949 }
950 {
robertphillips9f1c2412014-06-09 06:25:34 -0700951 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000952 canvas->clipPath(path, SkRegion::kIntersect_Op);
953 canvas->clipPath(invPath, SkRegion::kUnion_Op);
954 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
955 REPORTER_ASSERT(reporter, true == nonEmpty);
956 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
957 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
958 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
959 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
960 }
961 {
robertphillips9f1c2412014-06-09 06:25:34 -0700962 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000963 canvas->clipPath(path, SkRegion::kDifference_Op);
964 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
965 REPORTER_ASSERT(reporter, true == nonEmpty);
966 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
967 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
968 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
969 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
970 }
971 {
robertphillips9f1c2412014-06-09 06:25:34 -0700972 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000973 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
974 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
975 // True clip is actually empty in this case, but the best
976 // determination we can make using only bounds as input is that the
977 // clip is included in the bounds of 'path'.
978 REPORTER_ASSERT(reporter, true == nonEmpty);
979 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
980 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
982 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
983 }
984 {
robertphillips9f1c2412014-06-09 06:25:34 -0700985 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000986 canvas->clipPath(path, SkRegion::kIntersect_Op);
987 canvas->clipPath(path2, SkRegion::kXOR_Op);
988 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
989 REPORTER_ASSERT(reporter, true == nonEmpty);
990 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
991 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
992 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
993 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
994 }
995}
996
schenneyeeff8bb2015-07-07 14:27:10 -0700997static void test_cull_rect_reset(skiatest::Reporter* reporter) {
998 SkPictureRecorder recorder;
999 SkRect bounds = SkRect::MakeWH(10, 10);
1000 SkRTreeFactory factory;
1001 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
1002 bounds = SkRect::MakeWH(100, 100);
1003 SkPaint paint;
1004 canvas->drawRect(bounds, paint);
1005 canvas->drawRect(bounds, paint);
mtkleineedc3342015-07-08 08:26:39 -07001006 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
1007 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -07001008 REPORTER_ASSERT(reporter, picture);
1009
1010 SkRect finalCullRect = picture->cullRect();
1011 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1012 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1013 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1014 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1015
1016 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1017 SkRect bbhCullRect = pictureBBH->getRootBound();
1018 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1019 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1020 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1021 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1022}
1023
1024
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001025/**
1026 * A canvas that records the number of clip commands.
1027 */
1028class ClipCountingCanvas : public SkCanvas {
1029public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001030 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001031 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001032 , fClipCount(0){
1033 }
1034
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001035 virtual void onClipRect(const SkRect& r,
1036 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001037 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001038 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001039 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001040 }
1041
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001042 virtual void onClipRRect(const SkRRect& rrect,
1043 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001044 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001045 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001046 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001047 }
1048
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001049 virtual void onClipPath(const SkPath& path,
1050 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001051 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001052 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001053 this->INHERITED::onClipPath(path, op, edgeStyle);
1054 }
1055
mtklein36352bf2015-03-25 18:17:31 -07001056 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001057 fClipCount += 1;
1058 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001059 }
1060
1061 unsigned getClipCount() const { return fClipCount; }
1062
1063private:
1064 unsigned fClipCount;
1065
1066 typedef SkCanvas INHERITED;
1067};
1068
1069static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001070 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001071 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001072
1073 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1074 // The following expanding clip should not be skipped.
1075 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1076 // Draw something so the optimizer doesn't just fold the world.
1077 SkPaint p;
1078 p.setColor(SK_ColorBLUE);
1079 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001080 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001081
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001082 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001083 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001084
1085 // Both clips should be present on playback.
1086 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1087}
1088
tomhudson@google.com381010e2013-10-24 11:12:47 +00001089static void test_hierarchical(skiatest::Reporter* reporter) {
1090 SkBitmap bm;
1091 make_bm(&bm, 10, 10, SK_ColorRED, true);
1092
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001093 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001094
robertphillips9f1c2412014-06-09 06:25:34 -07001095 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001096 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1097 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001098
robertphillips9f1c2412014-06-09 06:25:34 -07001099 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001100 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1101 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001102
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001103 {
robertphillips9f1c2412014-06-09 06:25:34 -07001104 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001105 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001106 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1107 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1108 }
1109 {
robertphillips9f1c2412014-06-09 06:25:34 -07001110 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001111 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001112 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1113 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1114 }
1115 {
robertphillips9f1c2412014-06-09 06:25:34 -07001116 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001117 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001118 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001119 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1120 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1121 }
1122 {
robertphillips9f1c2412014-06-09 06:25:34 -07001123 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001124 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001125 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001126 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1127 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1128 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001129}
1130
robertphillips@google.comd5500882014-04-02 23:51:13 +00001131static void test_gen_id(skiatest::Reporter* reporter) {
1132
Robert Phillipscfaeec42014-07-13 12:00:50 -04001133 SkPictureRecorder recorder;
1134 recorder.beginRecording(0, 0);
1135 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001136
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001137 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001138 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001139
robertphillips9f1c2412014-06-09 06:25:34 -07001140 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001141 canvas->drawARGB(255, 255, 255, 255);
1142 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1143 // picture should have a non-zero id after recording
1144 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001145
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001146 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001147 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001148}
1149
caryclark5ef194c2015-08-31 09:22:38 -07001150static void test_typeface(skiatest::Reporter* reporter) {
1151 SkPictureRecorder recorder;
1152 SkCanvas* canvas = recorder.beginRecording(10, 10);
1153 SkPaint paint;
1154 paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic));
1155 canvas->drawText("Q", 1, 0, 10, paint);
1156 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1157 REPORTER_ASSERT(reporter, picture->hasText());
1158 SkDynamicMemoryWStream stream;
1159 picture->serialize(&stream);
1160}
1161
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001162DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -07001163 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001164#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001165 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001166 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001167#else
1168 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001169#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001170 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001171 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001172#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001173 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001174#endif
mtklein8e126562014-10-01 09:29:35 -07001175 test_has_text(reporter);
mtkleina16af212015-08-26 08:14:52 -07001176 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -07001177 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001178 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001179 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001180 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001181 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001182 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001183 test_savelayer_extraction(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -07001184 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001185}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001186
1187static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1188 const SkPaint paint;
1189 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1190 const SkIRect irect = { 2, 2, 3, 3 };
1191
1192 // Don't care what these record, as long as they're legal.
1193 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001194 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001195 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -08001196 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001197}
1198
1199static void test_draw_bitmaps(SkCanvas* canvas) {
1200 SkBitmap empty;
1201 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001202 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001203 draw_bitmaps(empty, canvas);
1204}
1205
1206DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001207 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001208 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001209 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001210}
1211
1212DEF_TEST(Canvas_EmptyBitmap, r) {
1213 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001214 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001215 SkCanvas canvas(dst);
1216
1217 test_draw_bitmaps(&canvas);
1218}
dneto3f22e8c2014-07-30 15:42:22 -07001219
1220DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1221 // This test is from crbug.com/344987.
1222 // The commands are:
1223 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001224 // drawBitmapRect
1225 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001226 // restore
1227 // The bug was that this structure was modified so that:
1228 // - The saveLayer and restore were eliminated
1229 // - The alpha was only applied to the first drawBitmapRectToRect
1230
1231 // This test draws blue and red squares inside a 50% transparent
1232 // layer. Both colours should show up muted.
1233 // When the bug is present, the red square (the second bitmap)
1234 // shows upwith full opacity.
1235
1236 SkBitmap blueBM;
1237 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1238 SkBitmap redBM;
1239 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1240 SkPaint semiTransparent;
1241 semiTransparent.setAlpha(0x80);
1242
1243 SkPictureRecorder recorder;
1244 SkCanvas* canvas = recorder.beginRecording(100, 100);
1245 canvas->drawARGB(0, 0, 0, 0);
1246
1247 canvas->saveLayer(0, &semiTransparent);
1248 canvas->drawBitmap(blueBM, 25, 25);
1249 canvas->drawBitmap(redBM, 50, 50);
1250 canvas->restore();
1251
1252 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1253
1254 // Now replay the picture back on another canvas
1255 // and check a couple of its pixels.
1256 SkBitmap replayBM;
1257 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1258 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001259 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001260 replayCanvas.flush();
1261
1262 // With the bug present, at (55, 55) we would get a fully opaque red
1263 // intead of a dark red.
1264 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1265 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1266}
mtklein3e8232b2014-08-18 13:39:11 -07001267
1268struct CountingBBH : public SkBBoxHierarchy {
1269 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001270 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001271
schenney23d85932015-03-06 16:20:28 -08001272 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001273
mtkleinc6ad06a2015-08-19 09:51:00 -07001274 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001275 this->searchCalls++;
1276 }
1277
mtklein36352bf2015-03-25 18:17:31 -07001278 void insert(const SkRect[], int) override {}
1279 virtual size_t bytesUsed() const override { return 0; }
1280 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001281};
1282
1283class SpoonFedBBHFactory : public SkBBHFactory {
1284public:
1285 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001286 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001287 return SkRef(fBBH);
1288 }
1289private:
1290 SkBBoxHierarchy* fBBH;
1291};
1292
1293// When the canvas clip covers the full picture, we don't need to call the BBH.
1294DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001295 SkRect bound = SkRect::MakeWH(320, 240);
1296 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001297 SpoonFedBBHFactory factory(&bbh);
1298
1299 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001300 SkCanvas* c = recorder.beginRecording(bound, &factory);
1301 // Record a few ops so we don't hit a small- or empty- picture optimization.
1302 c->drawRect(bound, SkPaint());
1303 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001304 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1305
1306 SkCanvas big(640, 480), small(300, 200);
1307
robertphillipsc5ba71d2014-09-04 08:42:50 -07001308 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001309 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1310
robertphillipsc5ba71d2014-09-04 08:42:50 -07001311 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001312 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1313}
mtkleind72094d2014-08-27 12:12:23 -07001314
1315DEF_TEST(Picture_BitmapLeak, r) {
1316 SkBitmap mut, immut;
1317 mut.allocN32Pixels(300, 200);
1318 immut.allocN32Pixels(300, 200);
1319 immut.setImmutable();
1320 SkASSERT(!mut.isImmutable());
1321 SkASSERT(immut.isImmutable());
1322
1323 // No one can hold a ref on our pixels yet.
1324 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1325 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1326
reed1bdfd3f2014-11-24 14:41:51 -08001327 SkAutoTUnref<const SkPicture> pic;
1328 {
1329 // we want the recorder to go out of scope before our subsequent checks, so we
1330 // place it inside local braces.
1331 SkPictureRecorder rec;
1332 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1333 canvas->drawBitmap(mut, 0, 0);
1334 canvas->drawBitmap(immut, 800, 600);
1335 pic.reset(rec.endRecording());
1336 }
mtkleind72094d2014-08-27 12:12:23 -07001337
1338 // The picture shares the immutable pixels but copies the mutable ones.
1339 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1340 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1341
1342 // When the picture goes away, it's just our bitmaps holding the refs.
halcanary96fcdcc2015-08-27 07:41:13 -07001343 pic.reset(nullptr);
mtkleind72094d2014-08-27 12:12:23 -07001344 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1345 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1346}
mtkleinfeaadee2015-04-08 11:25:48 -07001347
1348// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1349DEF_TEST(Picture_getRecordingCanvas, r) {
1350 SkPictureRecorder rec;
1351 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1352 for (int i = 0; i < 3; i++) {
1353 rec.beginRecording(100, 100);
1354 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1355 rec.endRecording()->unref();
1356 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1357 }
1358}
mtklein9db912c2015-05-19 11:11:26 -07001359
1360DEF_TEST(MiniRecorderLeftHanging, r) {
1361 // Any shader or other ref-counted effect will do just fine here.
1362 SkPaint paint;
1363 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1364
1365 SkMiniRecorder rec;
1366 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1367 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1368}
fmalita2ecc0002015-07-14 13:12:25 -07001369
1370DEF_TEST(Picture_preserveCullRect, r) {
1371 SkPictureRecorder recorder;
1372
1373 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1374 c->clear(SK_ColorCYAN);
1375
1376 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1377 SkDynamicMemoryWStream wstream;
1378 picture->serialize(&wstream);
1379
1380 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1381 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1382
1383 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1384 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1385 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1386 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1387 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1388}