blob: 3d05a824a26556ad02c281b21723bcc2fdda8567 [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) {
reede3b38ce2016-01-08 09:18:44 -080049 // We just need _some_ SkImage
50 const SkPMColor pixel = 0;
51 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
52 SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(info, &pixel, sizeof(pixel)));
mtkleina16af212015-08-26 08:14:52 -070053
54 SkPictureRecorder recorder;
reede3b38ce2016-01-08 09:18:44 -080055 recorder.beginRecording(100,100)->drawImage(image, 0,0);
mtkleina16af212015-08-26 08:14:52 -070056 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
57
58 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
59}
60
tomhudson3a0f2792014-08-20 05:29:41 -070061/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070062static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070063 SkPictureRecorder recorder;
64
mtklein8e126562014-10-01 09:29:35 -070065 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070066 {
67 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
68 }
69 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
70 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
71
mtklein8e126562014-10-01 09:29:35 -070072 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070073 {
74 SkPaint paint;
75 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
76 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
77 SkBitmap bitmap;
78 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
79 bitmap.eraseColor(SK_ColorBLUE);
80 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
81 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
82 SkShader::kClamp_TileMode);
83 paint.setShader(shader)->unref();
reedf5822822015-08-19 11:46:38 -070084 REPORTER_ASSERT(reporter, shader->isABitmap());
tomhudson3a0f2792014-08-20 05:29:41 -070085
86 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
87 }
88 picture.reset(recorder.endRecording());
89 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
90}
91
92
scroggo@google.comd614c6a2012-09-14 17:26:37 +000093#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070094// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070095// in debug mode, so only run in debug mode.
96static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000097 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000098 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070099 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -0700100 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000101 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -0700102 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -0700103 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000104}
105
106// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
107static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000108 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700109 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000110 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000111 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000112 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000113}
114#endif
115
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000116static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000117 SkPaint paint;
118 SkRect rect = SkRect::MakeWH(50, 50);
119
120 SkScalar unit = rand.nextUScalar1();
121 if (unit <= 0.3) {
122// SkDebugf("save\n");
123 canvas->save();
124 } else if (unit <= 0.6) {
125// SkDebugf("restore\n");
126 canvas->restore();
127 } else if (unit <= 0.9) {
128// SkDebugf("clip\n");
129 canvas->clipRect(rect);
130 } else {
131// SkDebugf("draw\n");
132 canvas->drawPaint(paint);
133 }
134}
135
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000136#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700137
mtklein8e126562014-10-01 09:29:35 -0700138static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000139 SkPictureRecorder recorder;
140
mtklein8e126562014-10-01 09:29:35 -0700141 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000142 {
143 SkPath path;
144 path.moveTo(0, 0);
145 path.lineTo(50, 50);
146
147 SkScalar intervals[] = { 1.0f, 1.0f };
reed5e1ddb12015-12-21 08:52:45 -0800148 SkAutoTUnref<SkPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000149
150 SkPaint paint;
151 paint.setStyle(SkPaint::kStroke_Style);
152 paint.setPathEffect(dash);
153
robertphillips98b03152015-01-26 11:29:36 -0800154 for (int i = 0; i < 50; ++i) {
155 canvas->drawPath(path, paint);
156 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000157 }
158 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
159 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000160
halcanary96fcdcc2015-08-27 07:41:13 -0700161 const char *reason = nullptr;
162 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700163 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000164
mtklein8e126562014-10-01 09:29:35 -0700165 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000166 {
167 SkPath path;
168
169 path.moveTo(0, 0);
170 path.lineTo(0, 50);
171 path.lineTo(25, 25);
172 path.lineTo(50, 50);
173 path.lineTo(50, 0);
174 path.close();
175 REPORTER_ASSERT(reporter, !path.isConvex());
176
177 SkPaint paint;
178 paint.setAntiAlias(true);
179 for (int i = 0; i < 50; ++i) {
180 canvas->drawPath(path, paint);
181 }
182 }
183 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800184 // A lot of small AA concave paths should be fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700185 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
jvanverthd86b07a2014-11-04 08:50:15 -0800186
187 canvas = recorder.beginRecording(100, 100);
188 {
189 SkPath path;
190
191 path.moveTo(0, 0);
192 path.lineTo(0, 100);
193 path.lineTo(50, 50);
194 path.lineTo(100, 100);
195 path.lineTo(100, 0);
196 path.close();
197 REPORTER_ASSERT(reporter, !path.isConvex());
198
199 SkPaint paint;
200 paint.setAntiAlias(true);
201 for (int i = 0; i < 50; ++i) {
202 canvas->drawPath(path, paint);
203 }
204 }
205 picture.reset(recorder.endRecording());
206 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700207 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000208
mtklein8e126562014-10-01 09:29:35 -0700209 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000210 {
211 SkPath path;
212
213 path.moveTo(0, 0);
214 path.lineTo(0, 50);
215 path.lineTo(25, 25);
216 path.lineTo(50, 50);
217 path.lineTo(50, 0);
218 path.close();
219 REPORTER_ASSERT(reporter, !path.isConvex());
220
221 SkPaint paint;
222 paint.setAntiAlias(true);
223 paint.setStyle(SkPaint::kStroke_Style);
224 paint.setStrokeWidth(0);
225 for (int i = 0; i < 50; ++i) {
226 canvas->drawPath(path, paint);
227 }
228 }
229 picture.reset(recorder.endRecording());
230 // hairline stroked AA concave paths are fine for GPU rendering
halcanary96fcdcc2015-08-27 07:41:13 -0700231 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700232
mtklein8e126562014-10-01 09:29:35 -0700233 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700234 {
235 SkPaint paint;
236 SkScalar intervals [] = { 10, 20 };
237 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
238 paint.setPathEffect(pe)->unref();
239
240 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800241
242 for (int i = 0; i < 50; ++i) {
243 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
244 }
tomhudson3a0f2792014-08-20 05:29:41 -0700245 }
246 picture.reset(recorder.endRecording());
247 // fast-path dashed effects are fine for GPU rendering ...
halcanary96fcdcc2015-08-27 07:41:13 -0700248 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
tomhudson3a0f2792014-08-20 05:29:41 -0700249
mtklein8e126562014-10-01 09:29:35 -0700250 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700251 {
252 SkPaint paint;
253 SkScalar intervals [] = { 10, 20 };
254 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
255 paint.setPathEffect(pe)->unref();
256
robertphillips98b03152015-01-26 11:29:36 -0800257 for (int i = 0; i < 50; ++i) {
258 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
259 }
tomhudson3a0f2792014-08-20 05:29:41 -0700260 }
261 picture.reset(recorder.endRecording());
262 // ... but only when applied to drawPoint() calls
halcanary96fcdcc2015-08-27 07:41:13 -0700263 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
mtklein53fecfb2014-08-21 09:11:37 -0700264
265 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700266 canvas = recorder.beginRecording(100, 100);
267 {
268 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700269 }
mtklein8e126562014-10-01 09:29:35 -0700270 picture.reset(recorder.endRecording());
halcanary96fcdcc2015-08-27 07:41:13 -0700271 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000272}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000273
robertphillips82365912014-11-12 09:32:34 -0800274#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000275
robertphillips82365912014-11-12 09:32:34 -0800276static void test_savelayer_extraction(skiatest::Reporter* reporter) {
277 static const int kWidth = 100;
278 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000279
robertphillips82365912014-11-12 09:32:34 -0800280 // Create complex paint that the bounding box computation code can't
281 // optimize away
282 SkScalar blueToRedMatrix[20] = { 0 };
283 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
284 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
285 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000286
robertphillips82365912014-11-12 09:32:34 -0800287 SkPaint complexPaint;
288 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000289
robertphillips82365912014-11-12 09:32:34 -0800290 SkAutoTUnref<SkPicture> pict, child;
291 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000292
robertphillips82365912014-11-12 09:32:34 -0800293 {
294 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700295
robertphillips82365912014-11-12 09:32:34 -0800296 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
297 &bbhFactory,
298 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700299
halcanary96fcdcc2015-08-27 07:41:13 -0700300 c->saveLayer(nullptr, &complexPaint);
robertphillips82365912014-11-12 09:32:34 -0800301 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700302
robertphillips82365912014-11-12 09:32:34 -0800303 child.reset(recorder.endRecording());
304 }
robertphillipsd6283302014-08-27 11:53:28 -0700305
robertphillips82365912014-11-12 09:32:34 -0800306 // create a picture with the structure:
307 // 1)
308 // SaveLayer
309 // Restore
310 // 2)
311 // SaveLayer
312 // Translate
313 // SaveLayer w/ bound
314 // Restore
315 // Restore
316 // 3)
317 // SaveLayer w/ copyable paint
318 // Restore
319 // 4)
320 // SaveLayer
321 // DrawPicture (which has a SaveLayer/Restore pair)
322 // Restore
323 // 5)
324 // SaveLayer
325 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
326 // Restore
327 {
328 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700329
robertphillips82365912014-11-12 09:32:34 -0800330 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
331 SkIntToScalar(kHeight),
332 &bbhFactory,
333 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000334 // 1)
halcanary96fcdcc2015-08-27 07:41:13 -0700335 c->saveLayer(nullptr, &complexPaint); // layer #0
robertphillips82365912014-11-12 09:32:34 -0800336 c->restore();
337
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000338 // 2)
halcanary96fcdcc2015-08-27 07:41:13 -0700339 c->saveLayer(nullptr, nullptr); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800340 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800341 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
342 c->saveLayer(&r, &complexPaint); // layer #2
343 c->restore();
344 c->restore();
345
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000346 // 3)
robertphillips82365912014-11-12 09:32:34 -0800347 {
halcanary96fcdcc2015-08-27 07:41:13 -0700348 c->saveLayer(nullptr, &complexPaint); // layer #3
robertphillips82365912014-11-12 09:32:34 -0800349 c->restore();
350 }
351
352 SkPaint layerPaint;
353 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000354 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000355 {
halcanary96fcdcc2015-08-27 07:41:13 -0700356 c->saveLayer(nullptr, &layerPaint); // layer #4
robertphillips82365912014-11-12 09:32:34 -0800357 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000358 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800359 }
360 // 5
361 {
362 SkPaint picturePaint;
363 SkMatrix trans;
364 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700365
halcanary96fcdcc2015-08-27 07:41:13 -0700366 c->saveLayer(nullptr, &layerPaint); // layer #6
robertphillips82365912014-11-12 09:32:34 -0800367 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700368 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000369 }
370
robertphillips82365912014-11-12 09:32:34 -0800371 pict.reset(recorder.endRecording());
372 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000373
robertphillips82365912014-11-12 09:32:34 -0800374 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800375 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
mtklein9db912c2015-05-19 11:11:26 -0700376 const SkBigPicture* bp = pict->asSkBigPicture();
377 REPORTER_ASSERT(reporter, bp);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000378
mtklein9db912c2015-05-19 11:11:26 -0700379 const SkBigPicture::AccelData* data = bp->accelData();
robertphillips82365912014-11-12 09:32:34 -0800380 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000381
robertphillips82365912014-11-12 09:32:34 -0800382 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
383 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700384
robertphillips82365912014-11-12 09:32:34 -0800385 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
386 // The parent/child layers appear in reverse order
387 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
388 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700389
robertphillips82365912014-11-12 09:32:34 -0800390 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700391
robertphillips82365912014-11-12 09:32:34 -0800392 // The parent/child layers appear in reverse order
393 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
394 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000395
robertphillips82365912014-11-12 09:32:34 -0800396 // The parent/child layers appear in reverse order
397 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
398 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000399
halcanary96fcdcc2015-08-27 07:41:13 -0700400 REPORTER_ASSERT(reporter, nullptr == info0.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800401 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
402 kHeight == info0.fBounds.height());
403 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
404 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
405 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700406 REPORTER_ASSERT(reporter, nullptr != info0.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800407 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000408
halcanary96fcdcc2015-08-27 07:41:13 -0700409 REPORTER_ASSERT(reporter, nullptr == info1.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800410 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
411 kHeight/2.0 == info1.fBounds.height());
412 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
413 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800414 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800415 kHeight/2.0 == info1.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700416 REPORTER_ASSERT(reporter, nullptr == info1.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800417 REPORTER_ASSERT(reporter, !info1.fIsNested &&
418 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000419
halcanary96fcdcc2015-08-27 07:41:13 -0700420 REPORTER_ASSERT(reporter, nullptr == info2.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800421 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
422 kHeight / 2 == info2.fBounds.height()); // bound reduces size
423 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
424 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
425 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
426 kHeight / 2 == info2.fBounds.fTop);
halcanary96fcdcc2015-08-27 07:41:13 -0700427 REPORTER_ASSERT(reporter, nullptr != info2.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800428 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000429
halcanary96fcdcc2015-08-27 07:41:13 -0700430 REPORTER_ASSERT(reporter, nullptr == info3.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800431 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
432 kHeight == info3.fBounds.height());
433 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
434 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
435 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
436 REPORTER_ASSERT(reporter, info3.fPaint);
437 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700438
halcanary96fcdcc2015-08-27 07:41:13 -0700439 REPORTER_ASSERT(reporter, nullptr == info4.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800440 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
441 kHeight == info4.fBounds.height());
442 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
443 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
444 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
445 REPORTER_ASSERT(reporter, info4.fPaint);
446 REPORTER_ASSERT(reporter, !info4.fIsNested &&
447 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700448
robertphillips82365912014-11-12 09:32:34 -0800449 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
450 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
451 kHeight == info5.fBounds.height());
452 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
453 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
454 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700455 REPORTER_ASSERT(reporter, nullptr != info5.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800456 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700457
halcanary96fcdcc2015-08-27 07:41:13 -0700458 REPORTER_ASSERT(reporter, nullptr == info6.fPicture);
robertphillips82365912014-11-12 09:32:34 -0800459 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
460 kHeight-10 == info6.fBounds.height());
461 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
462 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
463 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
464 REPORTER_ASSERT(reporter, info6.fPaint);
465 REPORTER_ASSERT(reporter, !info6.fIsNested &&
466 info6.fHasNestedLayers); // has a nested SL
467
468 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
469 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
470 kHeight == info7.fBounds.height());
471 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
472 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
473 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
halcanary96fcdcc2015-08-27 07:41:13 -0700474 REPORTER_ASSERT(reporter, nullptr != info7.fPaint);
robertphillips82365912014-11-12 09:32:34 -0800475 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000476 }
477}
478
mtklein8e126562014-10-01 09:29:35 -0700479static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700480 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700481
mtklein8e126562014-10-01 09:29:35 -0700482 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700483 {
mtkleinc551d9f2014-08-20 08:09:46 -0700484 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700485 }
486 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
487 REPORTER_ASSERT(reporter, !picture->hasText());
488
mtkleinc551d9f2014-08-20 08:09:46 -0700489 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700490 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700491 {
mtkleinc551d9f2014-08-20 08:09:46 -0700492 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700493 }
494 picture.reset(recorder.endRecording());
495 REPORTER_ASSERT(reporter, picture->hasText());
496
mtklein8e126562014-10-01 09:29:35 -0700497 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700498 {
mtkleinc551d9f2014-08-20 08:09:46 -0700499 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700500 }
501 picture.reset(recorder.endRecording());
502 REPORTER_ASSERT(reporter, picture->hasText());
503
mtklein8e126562014-10-01 09:29:35 -0700504 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700505 {
mtkleinc551d9f2014-08-20 08:09:46 -0700506 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700507 }
508 picture.reset(recorder.endRecording());
509 REPORTER_ASSERT(reporter, picture->hasText());
510
mtklein8e126562014-10-01 09:29:35 -0700511 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700512 {
513 SkPath path;
514 path.moveTo(0, 0);
515 path.lineTo(50, 50);
516
mtkleinc551d9f2014-08-20 08:09:46 -0700517 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700518 }
519 picture.reset(recorder.endRecording());
520 REPORTER_ASSERT(reporter, picture->hasText());
521
mtklein8e126562014-10-01 09:29:35 -0700522 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700523 {
524 SkPath path;
525 path.moveTo(0, 0);
526 path.lineTo(50, 50);
527
halcanary96fcdcc2015-08-27 07:41:13 -0700528 canvas->drawTextOnPath("Q", 1, path, nullptr, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700529 }
530 picture.reset(recorder.endRecording());
531 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700532
533 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700534 canvas = recorder.beginRecording(100,100);
535 {
536 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700537 }
mtklein8e126562014-10-01 09:29:35 -0700538 picture.reset(recorder.endRecording());
539 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700540}
541
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000542static void set_canvas_to_save_count_4(SkCanvas* canvas) {
543 canvas->restoreToCount(1);
544 canvas->save();
545 canvas->save();
546 canvas->save();
547}
548
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000549/**
550 * A canvas that records the number of saves, saveLayers and restores.
551 */
552class SaveCountingCanvas : public SkCanvas {
553public:
554 SaveCountingCanvas(int width, int height)
555 : INHERITED(width, height)
556 , fSaveCount(0)
557 , fSaveLayerCount(0)
558 , fRestoreCount(0){
559 }
560
reed4960eee2015-12-18 07:09:18 -0800561 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000562 ++fSaveLayerCount;
reed4960eee2015-12-18 07:09:18 -0800563 return this->INHERITED::getSaveLayerStrategy(rec);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000564 }
565
mtklein36352bf2015-03-25 18:17:31 -0700566 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000567 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400568 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000569 }
570
mtklein36352bf2015-03-25 18:17:31 -0700571 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000572 ++fRestoreCount;
573 this->INHERITED::willRestore();
574 }
575
576 unsigned int getSaveCount() const { return fSaveCount; }
577 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
578 unsigned int getRestoreCount() const { return fRestoreCount; }
579
580private:
581 unsigned int fSaveCount;
582 unsigned int fSaveLayerCount;
583 unsigned int fRestoreCount;
584
585 typedef SkCanvas INHERITED;
586};
587
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000588void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000589 unsigned int numSaves, unsigned int numSaveLayers,
590 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700591 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700592 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000593
robertphillipsc5ba71d2014-09-04 08:42:50 -0700594 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000595
mtklein87c41382014-09-08 07:31:18 -0700596 // Optimizations may have removed these,
597 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
598 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
599 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
600 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000601}
602
603// This class exists so SkPicture can friend it and give it access to
604// the 'partialReplay' method.
605class SkPictureRecorderReplayTester {
606public:
607 static SkPicture* Copy(SkPictureRecorder* recorder) {
608 SkPictureRecorder recorder2;
609
robertphillips9f1c2412014-06-09 06:25:34 -0700610 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000611
612 recorder->partialReplay(canvas);
613
614 return recorder2.endRecording();
615 }
616};
617
robertphillips9058d602014-06-10 11:45:46 -0700618static void create_imbalance(SkCanvas* canvas) {
619 SkRect clipRect = SkRect::MakeWH(2, 2);
620 SkRect drawRect = SkRect::MakeWH(10, 10);
621 canvas->save();
622 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
623 canvas->translate(1.0f, 1.0f);
624 SkPaint p;
625 p.setColor(SK_ColorGREEN);
626 canvas->drawRect(drawRect, p);
627 // no restore
628}
629
630// This tests that replaying a potentially unbalanced picture into a canvas
631// doesn't affect the canvas' save count or matrix/clip state.
632static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
633 SkBitmap bm;
634 bm.allocN32Pixels(4, 3);
635 SkCanvas canvas(bm);
636
637 int beforeSaveCount = canvas.getSaveCount();
638
639 SkMatrix beforeMatrix = canvas.getTotalMatrix();
640
641 SkRect beforeClip;
642
643 canvas.getClipBounds(&beforeClip);
644
645 canvas.drawPicture(picture);
646
647 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
648 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
649
650 SkRect afterClip;
651
652 canvas.getClipBounds(&afterClip);
653
654 REPORTER_ASSERT(reporter, afterClip == beforeClip);
655}
656
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000657// Test out SkPictureRecorder::partialReplay
658DEF_TEST(PictureRecorder_replay, reporter) {
659 // check save/saveLayer state
660 {
661 SkPictureRecorder recorder;
662
robertphillips9f1c2412014-06-09 06:25:34 -0700663 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000664
halcanary96fcdcc2015-08-27 07:41:13 -0700665 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000666
667 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
668
669 // The extra save and restore comes from the Copy process.
670 check_save_state(reporter, copy, 2, 1, 3);
671
halcanary96fcdcc2015-08-27 07:41:13 -0700672 canvas->saveLayer(nullptr, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000673
674 SkAutoTUnref<SkPicture> final(recorder.endRecording());
675
676 check_save_state(reporter, final, 1, 2, 3);
677
678 // The copy shouldn't pick up any operations added after it was made
679 check_save_state(reporter, copy, 2, 1, 3);
680 }
681
682 // (partially) check leakage of draw ops
683 {
684 SkPictureRecorder recorder;
685
robertphillips9f1c2412014-06-09 06:25:34 -0700686 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000687
688 SkRect r = SkRect::MakeWH(5, 5);
689 SkPaint p;
690
691 canvas->drawRect(r, p);
692
693 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
694
695 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
696
697 SkBitmap bm;
698 make_bm(&bm, 10, 10, SK_ColorRED, true);
699
700 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700701 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000702
703 SkAutoTUnref<SkPicture> final(recorder.endRecording());
704 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
705
706 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
707
708 // The snapshot shouldn't pick up any operations added after it was made
709 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
710 }
robertphillips9058d602014-06-10 11:45:46 -0700711
712 // Recreate the Android partialReplay test case
713 {
714 SkPictureRecorder recorder;
715
halcanary96fcdcc2015-08-27 07:41:13 -0700716 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
robertphillips9058d602014-06-10 11:45:46 -0700717 create_imbalance(canvas);
718
719 int expectedSaveCount = canvas->getSaveCount();
720
721 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
722 check_balance(reporter, copy);
723
724 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
725
726 // End the recording of source to test the picture finalization
727 // process isn't complicated by the partialReplay step
728 SkAutoTUnref<SkPicture> final(recorder.endRecording());
729 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000730}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000731
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000732static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
733 SkCanvas testCanvas(100, 100);
734 set_canvas_to_save_count_4(&testCanvas);
735
736 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
737
738 SkPaint paint;
739 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
740
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000741 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000742
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000743 {
744 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700745 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000746 canvas->save();
747 canvas->translate(10, 10);
748 canvas->drawRect(rect, paint);
749 canvas->save();
750 canvas->translate(10, 10);
751 canvas->drawRect(rect, paint);
752 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
753
robertphillips9b14f262014-06-04 05:40:44 -0700754 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000755 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
756 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000757
758 set_canvas_to_save_count_4(&testCanvas);
759
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000760 {
761 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700762 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000763 canvas->save();
764 canvas->translate(10, 10);
765 canvas->drawRect(rect, paint);
766 canvas->save();
767 canvas->translate(10, 10);
768 canvas->drawRect(rect, paint);
769 canvas->restore();
770 canvas->restore();
771 canvas->restore();
772 canvas->restore();
773 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000774
robertphillips9b14f262014-06-04 05:40:44 -0700775 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000776 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
777 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000778
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000779 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000780
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000781 {
robertphillips9f1c2412014-06-09 06:25:34 -0700782 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000783 canvas->translate(10, 10);
784 canvas->drawRect(rect, paint);
785 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
786
robertphillips9b14f262014-06-04 05:40:44 -0700787 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000788 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
789 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
790 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000791}
792
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000793static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000794 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000795
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000796 SkPictureRecorder recorder;
797
reed@google.com21b519d2012-10-02 17:42:15 +0000798 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000799 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000800
robertphillips9f1c2412014-06-09 06:25:34 -0700801 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000802
803 for (int i = 0; i < 1000; ++i) {
804 rand_op(canvas, rand);
805 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000806 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000807
808 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000809 }
810
811 {
robertphillips9f1c2412014-06-09 06:25:34 -0700812 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000813 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000814
reed@google.com21b519d2012-10-02 17:42:15 +0000815 for (int i = 0; i < 100; ++i) {
816 canvas->save();
817 }
818 while (canvas->getSaveCount() > 1) {
819 canvas->clipRect(rect);
820 canvas->restore();
821 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000822 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000823 }
824}
825
scroggo@google.com4b90b112012-12-04 15:08:56 +0000826#ifndef SK_DEBUG
827// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
828// should never do this.
829static void test_bad_bitmap() {
830 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
831 // fail.
832 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000833 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000834 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700835 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000836 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000837 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000838
839 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700840 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000841}
842#endif
843
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000844static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000845 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700846 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700847 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000848 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000849 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
850
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000851 SkDynamicMemoryWStream wStream;
halcanaryf2848b62015-12-10 12:40:23 -0800852 SkAutoTUnref<SkPixelSerializer> serializer(
853 SkImageEncoder::CreatePixelSerializer());
854 picture->serialize(&wStream, serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000855 return wStream.copyToData();
856}
857
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000858struct ErrorContext {
859 int fErrors;
860 skiatest::Reporter* fReporter;
861};
862
863static void assert_one_parse_error_cb(SkError error, void* context) {
864 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
865 errorContext->fErrors++;
866 // This test only expects one error, and that is a kParseError. If there are others,
867 // there is some unknown problem.
868 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
869 "This threw more errors than expected.");
870 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
871 SkGetLastErrorString());
872}
873
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000874static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
875 // Create a bitmap that will be encoded.
876 SkBitmap original;
877 make_bm(&original, 100, 100, SK_ColorBLUE, true);
878 SkDynamicMemoryWStream wStream;
879 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
880 return;
881 }
882 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000883
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000884 SkBitmap bm;
reedd1146452015-09-25 06:56:57 -0700885 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000886 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000887
888 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
889 // Flattening original will follow the old path of performing an encode, while flattening bm
890 // will use the already encoded data.
891 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
892 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
893 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000894 // Now test that a parse error was generated when trying to create a new SkPicture without
895 // providing a function to decode the bitmap.
896 ErrorContext context;
897 context.fErrors = 0;
898 context.fReporter = reporter;
899 SkSetErrorCallback(assert_one_parse_error_cb, &context);
900 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000901 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700902 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, nullptr));
903 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000904 SkClearLastError();
halcanary96fcdcc2015-08-27 07:41:13 -0700905 SkSetErrorCallback(nullptr, nullptr);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000906}
907
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000908static void test_clip_bound_opt(skiatest::Reporter* reporter) {
909 // Test for crbug.com/229011
910 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
911 SkIntToScalar(2), SkIntToScalar(2));
912 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
913 SkIntToScalar(1), SkIntToScalar(1));
914 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
915 SkIntToScalar(1), SkIntToScalar(1));
916
917 SkPath invPath;
918 invPath.addOval(rect1);
919 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
920 SkPath path;
921 path.addOval(rect2);
922 SkPath path2;
923 path2.addOval(rect3);
924 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000925 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700926
927 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000928 {
robertphillips9f1c2412014-06-09 06:25:34 -0700929 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000930 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
931 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
932 REPORTER_ASSERT(reporter, true == nonEmpty);
933 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
934 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
935 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
936 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
937 }
938 {
robertphillips9f1c2412014-06-09 06:25:34 -0700939 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000940 canvas->clipPath(path, SkRegion::kIntersect_Op);
941 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
942 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
943 REPORTER_ASSERT(reporter, true == nonEmpty);
944 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
945 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
946 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
947 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
948 }
949 {
robertphillips9f1c2412014-06-09 06:25:34 -0700950 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000951 canvas->clipPath(path, SkRegion::kIntersect_Op);
952 canvas->clipPath(invPath, SkRegion::kUnion_Op);
953 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
954 REPORTER_ASSERT(reporter, true == nonEmpty);
955 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
956 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
957 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
958 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
959 }
960 {
robertphillips9f1c2412014-06-09 06:25:34 -0700961 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000962 canvas->clipPath(path, SkRegion::kDifference_Op);
963 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
964 REPORTER_ASSERT(reporter, true == nonEmpty);
965 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
966 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
967 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
968 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
969 }
970 {
robertphillips9f1c2412014-06-09 06:25:34 -0700971 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000972 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
973 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
974 // True clip is actually empty in this case, but the best
975 // determination we can make using only bounds as input is that the
976 // clip is included in the bounds of 'path'.
977 REPORTER_ASSERT(reporter, true == nonEmpty);
978 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
979 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
980 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
982 }
983 {
robertphillips9f1c2412014-06-09 06:25:34 -0700984 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000985 canvas->clipPath(path, SkRegion::kIntersect_Op);
986 canvas->clipPath(path2, SkRegion::kXOR_Op);
987 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
988 REPORTER_ASSERT(reporter, true == nonEmpty);
989 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
990 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
991 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
992 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
993 }
994}
995
schenneyeeff8bb2015-07-07 14:27:10 -0700996static void test_cull_rect_reset(skiatest::Reporter* reporter) {
997 SkPictureRecorder recorder;
998 SkRect bounds = SkRect::MakeWH(10, 10);
999 SkRTreeFactory factory;
1000 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
1001 bounds = SkRect::MakeWH(100, 100);
1002 SkPaint paint;
1003 canvas->drawRect(bounds, paint);
1004 canvas->drawRect(bounds, paint);
mtkleineedc3342015-07-08 08:26:39 -07001005 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
1006 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -07001007 REPORTER_ASSERT(reporter, picture);
1008
1009 SkRect finalCullRect = picture->cullRect();
1010 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1011 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1012 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1013 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1014
1015 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1016 SkRect bbhCullRect = pictureBBH->getRootBound();
1017 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1018 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1019 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1020 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1021}
1022
1023
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001024/**
1025 * A canvas that records the number of clip commands.
1026 */
1027class ClipCountingCanvas : public SkCanvas {
1028public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001029 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001030 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001031 , fClipCount(0){
1032 }
1033
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001034 virtual void onClipRect(const SkRect& r,
1035 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001036 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001037 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001038 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001039 }
1040
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001041 virtual void onClipRRect(const SkRRect& rrect,
1042 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001043 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001044 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001045 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001046 }
1047
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001048 virtual void onClipPath(const SkPath& path,
1049 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001050 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001051 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001052 this->INHERITED::onClipPath(path, op, edgeStyle);
1053 }
1054
mtklein36352bf2015-03-25 18:17:31 -07001055 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001056 fClipCount += 1;
1057 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001058 }
1059
1060 unsigned getClipCount() const { return fClipCount; }
1061
1062private:
1063 unsigned fClipCount;
1064
1065 typedef SkCanvas INHERITED;
1066};
1067
1068static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001069 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001070 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001071
1072 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1073 // The following expanding clip should not be skipped.
1074 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1075 // Draw something so the optimizer doesn't just fold the world.
1076 SkPaint p;
1077 p.setColor(SK_ColorBLUE);
1078 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001079 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001080
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001081 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001082 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001083
1084 // Both clips should be present on playback.
1085 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1086}
1087
tomhudson@google.com381010e2013-10-24 11:12:47 +00001088static void test_hierarchical(skiatest::Reporter* reporter) {
1089 SkBitmap bm;
1090 make_bm(&bm, 10, 10, SK_ColorRED, true);
1091
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001092 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001093
robertphillips9f1c2412014-06-09 06:25:34 -07001094 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001095 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1096 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001097
robertphillips9f1c2412014-06-09 06:25:34 -07001098 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001099 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1100 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001101
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001102 {
robertphillips9f1c2412014-06-09 06:25:34 -07001103 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001104 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001105 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1106 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1107 }
1108 {
robertphillips9f1c2412014-06-09 06:25:34 -07001109 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001110 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001111 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1112 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1113 }
1114 {
robertphillips9f1c2412014-06-09 06:25:34 -07001115 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001116 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001117 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001118 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1119 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1120 }
1121 {
robertphillips9f1c2412014-06-09 06:25:34 -07001122 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001123 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001124 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001125 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1126 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1127 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001128}
1129
robertphillips@google.comd5500882014-04-02 23:51:13 +00001130static void test_gen_id(skiatest::Reporter* reporter) {
1131
Robert Phillipscfaeec42014-07-13 12:00:50 -04001132 SkPictureRecorder recorder;
1133 recorder.beginRecording(0, 0);
1134 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001135
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001136 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001137 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001138
robertphillips9f1c2412014-06-09 06:25:34 -07001139 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001140 canvas->drawARGB(255, 255, 255, 255);
1141 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1142 // picture should have a non-zero id after recording
1143 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001144
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001145 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001146 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001147}
1148
caryclark5ef194c2015-08-31 09:22:38 -07001149static void test_typeface(skiatest::Reporter* reporter) {
1150 SkPictureRecorder recorder;
1151 SkCanvas* canvas = recorder.beginRecording(10, 10);
1152 SkPaint paint;
1153 paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic));
1154 canvas->drawText("Q", 1, 0, 10, paint);
1155 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1156 REPORTER_ASSERT(reporter, picture->hasText());
1157 SkDynamicMemoryWStream stream;
1158 picture->serialize(&stream);
1159}
1160
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001161DEF_TEST(Picture, reporter) {
caryclark5ef194c2015-08-31 09:22:38 -07001162 test_typeface(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001163#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001164 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001165 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001166#else
1167 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001168#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001169 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001170 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001171#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001172 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001173#endif
mtklein8e126562014-10-01 09:29:35 -07001174 test_has_text(reporter);
mtkleina16af212015-08-26 08:14:52 -07001175 test_images_are_found_by_willPlayBackBitmaps(reporter);
mtklein8e126562014-10-01 09:29:35 -07001176 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001177 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001178 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001179 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001180 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001181 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001182 test_savelayer_extraction(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -07001183 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001184}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001185
1186static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1187 const SkPaint paint;
1188 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1189 const SkIRect irect = { 2, 2, 3, 3 };
1190
1191 // Don't care what these record, as long as they're legal.
1192 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001193 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001194 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
reedda420b92015-12-16 08:38:15 -08001195 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001196}
1197
1198static void test_draw_bitmaps(SkCanvas* canvas) {
1199 SkBitmap empty;
1200 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001201 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001202 draw_bitmaps(empty, canvas);
1203}
1204
1205DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001206 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001207 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001208 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001209}
1210
1211DEF_TEST(Canvas_EmptyBitmap, r) {
1212 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001213 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001214 SkCanvas canvas(dst);
1215
1216 test_draw_bitmaps(&canvas);
1217}
dneto3f22e8c2014-07-30 15:42:22 -07001218
1219DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1220 // This test is from crbug.com/344987.
1221 // The commands are:
1222 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001223 // drawBitmapRect
1224 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001225 // restore
1226 // The bug was that this structure was modified so that:
1227 // - The saveLayer and restore were eliminated
1228 // - The alpha was only applied to the first drawBitmapRectToRect
1229
1230 // This test draws blue and red squares inside a 50% transparent
1231 // layer. Both colours should show up muted.
1232 // When the bug is present, the red square (the second bitmap)
1233 // shows upwith full opacity.
1234
1235 SkBitmap blueBM;
1236 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1237 SkBitmap redBM;
1238 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1239 SkPaint semiTransparent;
1240 semiTransparent.setAlpha(0x80);
1241
1242 SkPictureRecorder recorder;
1243 SkCanvas* canvas = recorder.beginRecording(100, 100);
1244 canvas->drawARGB(0, 0, 0, 0);
1245
1246 canvas->saveLayer(0, &semiTransparent);
1247 canvas->drawBitmap(blueBM, 25, 25);
1248 canvas->drawBitmap(redBM, 50, 50);
1249 canvas->restore();
1250
1251 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1252
1253 // Now replay the picture back on another canvas
1254 // and check a couple of its pixels.
1255 SkBitmap replayBM;
1256 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1257 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001258 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001259 replayCanvas.flush();
1260
1261 // With the bug present, at (55, 55) we would get a fully opaque red
1262 // intead of a dark red.
1263 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1264 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1265}
mtklein3e8232b2014-08-18 13:39:11 -07001266
1267struct CountingBBH : public SkBBoxHierarchy {
1268 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001269 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001270
schenney23d85932015-03-06 16:20:28 -08001271 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001272
mtkleinc6ad06a2015-08-19 09:51:00 -07001273 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001274 this->searchCalls++;
1275 }
1276
mtklein36352bf2015-03-25 18:17:31 -07001277 void insert(const SkRect[], int) override {}
1278 virtual size_t bytesUsed() const override { return 0; }
1279 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001280};
1281
1282class SpoonFedBBHFactory : public SkBBHFactory {
1283public:
1284 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001285 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001286 return SkRef(fBBH);
1287 }
1288private:
1289 SkBBoxHierarchy* fBBH;
1290};
1291
1292// When the canvas clip covers the full picture, we don't need to call the BBH.
1293DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001294 SkRect bound = SkRect::MakeWH(320, 240);
1295 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001296 SpoonFedBBHFactory factory(&bbh);
1297
1298 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001299 SkCanvas* c = recorder.beginRecording(bound, &factory);
1300 // Record a few ops so we don't hit a small- or empty- picture optimization.
1301 c->drawRect(bound, SkPaint());
1302 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001303 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1304
1305 SkCanvas big(640, 480), small(300, 200);
1306
robertphillipsc5ba71d2014-09-04 08:42:50 -07001307 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001308 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1309
robertphillipsc5ba71d2014-09-04 08:42:50 -07001310 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001311 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1312}
mtkleind72094d2014-08-27 12:12:23 -07001313
1314DEF_TEST(Picture_BitmapLeak, r) {
1315 SkBitmap mut, immut;
1316 mut.allocN32Pixels(300, 200);
1317 immut.allocN32Pixels(300, 200);
1318 immut.setImmutable();
1319 SkASSERT(!mut.isImmutable());
1320 SkASSERT(immut.isImmutable());
1321
1322 // No one can hold a ref on our pixels yet.
1323 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1324 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1325
reed1bdfd3f2014-11-24 14:41:51 -08001326 SkAutoTUnref<const SkPicture> pic;
1327 {
1328 // we want the recorder to go out of scope before our subsequent checks, so we
1329 // place it inside local braces.
1330 SkPictureRecorder rec;
1331 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1332 canvas->drawBitmap(mut, 0, 0);
1333 canvas->drawBitmap(immut, 800, 600);
1334 pic.reset(rec.endRecording());
1335 }
mtkleind72094d2014-08-27 12:12:23 -07001336
1337 // The picture shares the immutable pixels but copies the mutable ones.
1338 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1339 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1340
1341 // When the picture goes away, it's just our bitmaps holding the refs.
halcanary96fcdcc2015-08-27 07:41:13 -07001342 pic.reset(nullptr);
mtkleind72094d2014-08-27 12:12:23 -07001343 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1344 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1345}
mtkleinfeaadee2015-04-08 11:25:48 -07001346
1347// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1348DEF_TEST(Picture_getRecordingCanvas, r) {
1349 SkPictureRecorder rec;
1350 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1351 for (int i = 0; i < 3; i++) {
1352 rec.beginRecording(100, 100);
1353 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1354 rec.endRecording()->unref();
1355 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1356 }
1357}
mtklein9db912c2015-05-19 11:11:26 -07001358
1359DEF_TEST(MiniRecorderLeftHanging, r) {
1360 // Any shader or other ref-counted effect will do just fine here.
1361 SkPaint paint;
1362 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1363
1364 SkMiniRecorder rec;
1365 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1366 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1367}
fmalita2ecc0002015-07-14 13:12:25 -07001368
1369DEF_TEST(Picture_preserveCullRect, r) {
1370 SkPictureRecorder recorder;
1371
1372 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1373 c->clear(SK_ColorCYAN);
1374
1375 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1376 SkDynamicMemoryWStream wstream;
1377 picture->serialize(&wstream);
1378
1379 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1380 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1381
1382 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1383 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1384 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1385 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1386 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1387}