blob: 2afb0dd73d1ebe5762a76ed4f40986e355768730 [file] [log] [blame]
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
mtklein3e8232b2014-08-18 13:39:11 -07008#include "SkBBoxHierarchy.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +00009#include "SkBlurImageFilter.h"
reed@google.com21b519d2012-10-02 17:42:15 +000010#include "SkCanvas.h"
robertphillipsd8aa7b72014-10-30 16:45:02 -070011#include "SkColorMatrixFilter.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000012#include "SkColorPriv.h"
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +000013#include "SkDashPathEffect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000014#include "SkData.h"
reed5965c8a2015-01-07 18:04:45 -080015#include "SkImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000016#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000017#include "SkImageEncoder.h"
18#include "SkImageGenerator.h"
robertphillips82365912014-11-12 09:32:34 -080019#include "SkLayerInfo.h"
reed@google.com21b519d2012-10-02 17:42:15 +000020#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000021#include "SkPicture.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000022#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000023#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070024#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080025#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070026#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000027#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000028#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080029#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000030#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000031#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070032#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000033
34#if SK_SUPPORT_GPU
35#include "SkSurface.h"
36#include "GrContextFactory.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000037#endif
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000038#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000039
reed@google.com47b679b2014-05-14 18:58:16 +000040#include "SkLumaColorFilter.h"
41#include "SkColorFilterImageFilter.h"
42
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000044 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000045 bm->eraseColor(color);
46 if (immutable) {
47 bm->setImmutable();
48 }
49}
50
tomhudson3a0f2792014-08-20 05:29:41 -070051/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070052static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070053 SkPictureRecorder recorder;
54
mtklein8e126562014-10-01 09:29:35 -070055 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070056 {
57 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
58 }
59 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
60 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
61
mtklein8e126562014-10-01 09:29:35 -070062 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070063 {
64 SkPaint paint;
65 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
66 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
67 SkBitmap bitmap;
68 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
69 bitmap.eraseColor(SK_ColorBLUE);
70 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
71 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
72 SkShader::kClamp_TileMode);
73 paint.setShader(shader)->unref();
reedf5822822015-08-19 11:46:38 -070074 REPORTER_ASSERT(reporter, shader->isABitmap());
tomhudson3a0f2792014-08-20 05:29:41 -070075
76 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
77 }
78 picture.reset(recorder.endRecording());
79 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
80}
81
82
scroggo@google.comd614c6a2012-09-14 17:26:37 +000083#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070084// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070085// in debug mode, so only run in debug mode.
86static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000087 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000088 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070089 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070090 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +000091 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -070092 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070093 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000094}
95
96// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
97static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000098 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070099 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000100 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000101 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000102 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000103}
104#endif
105
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000106static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000107 SkPaint paint;
108 SkRect rect = SkRect::MakeWH(50, 50);
109
110 SkScalar unit = rand.nextUScalar1();
111 if (unit <= 0.3) {
112// SkDebugf("save\n");
113 canvas->save();
114 } else if (unit <= 0.6) {
115// SkDebugf("restore\n");
116 canvas->restore();
117 } else if (unit <= 0.9) {
118// SkDebugf("clip\n");
119 canvas->clipRect(rect);
120 } else {
121// SkDebugf("draw\n");
122 canvas->drawPaint(paint);
123 }
124}
125
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000126#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700127
mtklein8e126562014-10-01 09:29:35 -0700128static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000129 SkPictureRecorder recorder;
130
mtklein8e126562014-10-01 09:29:35 -0700131 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000132 {
133 SkPath path;
134 path.moveTo(0, 0);
135 path.lineTo(50, 50);
136
137 SkScalar intervals[] = { 1.0f, 1.0f };
138 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
139
140 SkPaint paint;
141 paint.setStyle(SkPaint::kStroke_Style);
142 paint.setPathEffect(dash);
143
robertphillips98b03152015-01-26 11:29:36 -0800144 for (int i = 0; i < 50; ++i) {
145 canvas->drawPath(path, paint);
146 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000147 }
148 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
149 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000150
151 const char *reason = NULL;
152 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700153 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000154
mtklein8e126562014-10-01 09:29:35 -0700155 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000156 {
157 SkPath path;
158
159 path.moveTo(0, 0);
160 path.lineTo(0, 50);
161 path.lineTo(25, 25);
162 path.lineTo(50, 50);
163 path.lineTo(50, 0);
164 path.close();
165 REPORTER_ASSERT(reporter, !path.isConvex());
166
167 SkPaint paint;
168 paint.setAntiAlias(true);
169 for (int i = 0; i < 50; ++i) {
170 canvas->drawPath(path, paint);
171 }
172 }
173 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800174 // A lot of small AA concave paths should be fine for GPU rendering
175 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
176
177 canvas = recorder.beginRecording(100, 100);
178 {
179 SkPath path;
180
181 path.moveTo(0, 0);
182 path.lineTo(0, 100);
183 path.lineTo(50, 50);
184 path.lineTo(100, 100);
185 path.lineTo(100, 0);
186 path.close();
187 REPORTER_ASSERT(reporter, !path.isConvex());
188
189 SkPaint paint;
190 paint.setAntiAlias(true);
191 for (int i = 0; i < 50; ++i) {
192 canvas->drawPath(path, paint);
193 }
194 }
195 picture.reset(recorder.endRecording());
196 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000197 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
198
mtklein8e126562014-10-01 09:29:35 -0700199 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000200 {
201 SkPath path;
202
203 path.moveTo(0, 0);
204 path.lineTo(0, 50);
205 path.lineTo(25, 25);
206 path.lineTo(50, 50);
207 path.lineTo(50, 0);
208 path.close();
209 REPORTER_ASSERT(reporter, !path.isConvex());
210
211 SkPaint paint;
212 paint.setAntiAlias(true);
213 paint.setStyle(SkPaint::kStroke_Style);
214 paint.setStrokeWidth(0);
215 for (int i = 0; i < 50; ++i) {
216 canvas->drawPath(path, paint);
217 }
218 }
219 picture.reset(recorder.endRecording());
220 // hairline stroked AA concave paths are fine for GPU rendering
221 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
tomhudson3a0f2792014-08-20 05:29:41 -0700222
mtklein8e126562014-10-01 09:29:35 -0700223 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700224 {
225 SkPaint paint;
226 SkScalar intervals [] = { 10, 20 };
227 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
228 paint.setPathEffect(pe)->unref();
229
230 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800231
232 for (int i = 0; i < 50; ++i) {
233 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
234 }
tomhudson3a0f2792014-08-20 05:29:41 -0700235 }
236 picture.reset(recorder.endRecording());
237 // fast-path dashed effects are fine for GPU rendering ...
238 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
239
mtklein8e126562014-10-01 09:29:35 -0700240 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700241 {
242 SkPaint paint;
243 SkScalar intervals [] = { 10, 20 };
244 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
245 paint.setPathEffect(pe)->unref();
246
robertphillips98b03152015-01-26 11:29:36 -0800247 for (int i = 0; i < 50; ++i) {
248 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
249 }
tomhudson3a0f2792014-08-20 05:29:41 -0700250 }
251 picture.reset(recorder.endRecording());
252 // ... but only when applied to drawPoint() calls
253 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
mtklein53fecfb2014-08-21 09:11:37 -0700254
255 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700256 canvas = recorder.beginRecording(100, 100);
257 {
258 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700259 }
mtklein8e126562014-10-01 09:29:35 -0700260 picture.reset(recorder.endRecording());
261 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000262}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000263
robertphillips82365912014-11-12 09:32:34 -0800264#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000265
robertphillips82365912014-11-12 09:32:34 -0800266static void test_savelayer_extraction(skiatest::Reporter* reporter) {
267 static const int kWidth = 100;
268 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000269
robertphillips82365912014-11-12 09:32:34 -0800270 // Create complex paint that the bounding box computation code can't
271 // optimize away
272 SkScalar blueToRedMatrix[20] = { 0 };
273 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
274 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
275 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000276
robertphillips82365912014-11-12 09:32:34 -0800277 SkPaint complexPaint;
278 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000279
robertphillips82365912014-11-12 09:32:34 -0800280 SkAutoTUnref<SkPicture> pict, child;
281 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000282
robertphillips82365912014-11-12 09:32:34 -0800283 {
284 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700285
robertphillips82365912014-11-12 09:32:34 -0800286 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
287 &bbhFactory,
288 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700289
robertphillips82365912014-11-12 09:32:34 -0800290 c->saveLayer(NULL, &complexPaint);
291 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700292
robertphillips82365912014-11-12 09:32:34 -0800293 child.reset(recorder.endRecording());
294 }
robertphillipsd6283302014-08-27 11:53:28 -0700295
robertphillips82365912014-11-12 09:32:34 -0800296 // create a picture with the structure:
297 // 1)
298 // SaveLayer
299 // Restore
300 // 2)
301 // SaveLayer
302 // Translate
303 // SaveLayer w/ bound
304 // Restore
305 // Restore
306 // 3)
307 // SaveLayer w/ copyable paint
308 // Restore
309 // 4)
310 // SaveLayer
311 // DrawPicture (which has a SaveLayer/Restore pair)
312 // Restore
313 // 5)
314 // SaveLayer
315 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
316 // Restore
317 {
318 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700319
robertphillips82365912014-11-12 09:32:34 -0800320 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
321 SkIntToScalar(kHeight),
322 &bbhFactory,
323 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000324 // 1)
robertphillips82365912014-11-12 09:32:34 -0800325 c->saveLayer(NULL, &complexPaint); // layer #0
326 c->restore();
327
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000328 // 2)
robertphillips82365912014-11-12 09:32:34 -0800329 c->saveLayer(NULL, NULL); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800330 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800331 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
332 c->saveLayer(&r, &complexPaint); // layer #2
333 c->restore();
334 c->restore();
335
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000336 // 3)
robertphillips82365912014-11-12 09:32:34 -0800337 {
338 c->saveLayer(NULL, &complexPaint); // layer #3
339 c->restore();
340 }
341
342 SkPaint layerPaint;
343 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000344 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000345 {
robertphillips82365912014-11-12 09:32:34 -0800346 c->saveLayer(NULL, &layerPaint); // layer #4
347 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000348 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800349 }
350 // 5
351 {
352 SkPaint picturePaint;
353 SkMatrix trans;
354 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700355
robertphillips82365912014-11-12 09:32:34 -0800356 c->saveLayer(NULL, &layerPaint); // layer #6
357 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700358 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000359 }
360
robertphillips82365912014-11-12 09:32:34 -0800361 pict.reset(recorder.endRecording());
362 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000363
robertphillips82365912014-11-12 09:32:34 -0800364 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800365 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
mtklein9db912c2015-05-19 11:11:26 -0700366 const SkBigPicture* bp = pict->asSkBigPicture();
367 REPORTER_ASSERT(reporter, bp);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000368
mtklein9db912c2015-05-19 11:11:26 -0700369 const SkBigPicture::AccelData* data = bp->accelData();
robertphillips82365912014-11-12 09:32:34 -0800370 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000371
robertphillips82365912014-11-12 09:32:34 -0800372 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
373 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700374
robertphillips82365912014-11-12 09:32:34 -0800375 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
376 // The parent/child layers appear in reverse order
377 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
378 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700379
robertphillips82365912014-11-12 09:32:34 -0800380 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700381
robertphillips82365912014-11-12 09:32:34 -0800382 // The parent/child layers appear in reverse order
383 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
384 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000385
robertphillips82365912014-11-12 09:32:34 -0800386 // The parent/child layers appear in reverse order
387 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
388 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000389
robertphillips82365912014-11-12 09:32:34 -0800390 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
391 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
392 kHeight == info0.fBounds.height());
393 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
394 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
395 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
396 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
397 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000398
robertphillips82365912014-11-12 09:32:34 -0800399 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
400 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
401 kHeight/2.0 == info1.fBounds.height());
402 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
403 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800404 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800405 kHeight/2.0 == info1.fBounds.fTop);
406 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
407 REPORTER_ASSERT(reporter, !info1.fIsNested &&
408 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000409
robertphillips82365912014-11-12 09:32:34 -0800410 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
411 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
412 kHeight / 2 == info2.fBounds.height()); // bound reduces size
413 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
414 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
415 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
416 kHeight / 2 == info2.fBounds.fTop);
417 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
418 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000419
robertphillips82365912014-11-12 09:32:34 -0800420 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
421 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
422 kHeight == info3.fBounds.height());
423 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
424 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
425 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
426 REPORTER_ASSERT(reporter, info3.fPaint);
427 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700428
robertphillips82365912014-11-12 09:32:34 -0800429 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
430 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
431 kHeight == info4.fBounds.height());
432 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
433 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
434 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
435 REPORTER_ASSERT(reporter, info4.fPaint);
436 REPORTER_ASSERT(reporter, !info4.fIsNested &&
437 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700438
robertphillips82365912014-11-12 09:32:34 -0800439 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
440 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
441 kHeight == info5.fBounds.height());
442 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
443 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
444 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
445 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
446 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700447
robertphillips82365912014-11-12 09:32:34 -0800448 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
449 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
450 kHeight-10 == info6.fBounds.height());
451 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
452 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
453 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
454 REPORTER_ASSERT(reporter, info6.fPaint);
455 REPORTER_ASSERT(reporter, !info6.fIsNested &&
456 info6.fHasNestedLayers); // has a nested SL
457
458 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
459 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
460 kHeight == info7.fBounds.height());
461 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
462 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
463 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
464 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
465 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000466 }
467}
468
mtklein8e126562014-10-01 09:29:35 -0700469static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700470 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700471
mtklein8e126562014-10-01 09:29:35 -0700472 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700473 {
mtkleinc551d9f2014-08-20 08:09:46 -0700474 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700475 }
476 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
477 REPORTER_ASSERT(reporter, !picture->hasText());
478
mtkleinc551d9f2014-08-20 08:09:46 -0700479 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700480 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700481 {
mtkleinc551d9f2014-08-20 08:09:46 -0700482 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700483 }
484 picture.reset(recorder.endRecording());
485 REPORTER_ASSERT(reporter, picture->hasText());
486
mtklein8e126562014-10-01 09:29:35 -0700487 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700488 {
mtkleinc551d9f2014-08-20 08:09:46 -0700489 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700490 }
491 picture.reset(recorder.endRecording());
492 REPORTER_ASSERT(reporter, picture->hasText());
493
mtklein8e126562014-10-01 09:29:35 -0700494 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700495 {
mtkleinc551d9f2014-08-20 08:09:46 -0700496 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700497 }
498 picture.reset(recorder.endRecording());
499 REPORTER_ASSERT(reporter, picture->hasText());
500
mtklein8e126562014-10-01 09:29:35 -0700501 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700502 {
503 SkPath path;
504 path.moveTo(0, 0);
505 path.lineTo(50, 50);
506
mtkleinc551d9f2014-08-20 08:09:46 -0700507 canvas->drawTextOnPathHV("Q", 1, path, 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->drawTextOnPath("Q", 1, path, NULL, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700519 }
520 picture.reset(recorder.endRecording());
521 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700522
523 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700524 canvas = recorder.beginRecording(100,100);
525 {
526 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700527 }
mtklein8e126562014-10-01 09:29:35 -0700528 picture.reset(recorder.endRecording());
529 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700530}
531
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000532static void set_canvas_to_save_count_4(SkCanvas* canvas) {
533 canvas->restoreToCount(1);
534 canvas->save();
535 canvas->save();
536 canvas->save();
537}
538
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000539/**
540 * A canvas that records the number of saves, saveLayers and restores.
541 */
542class SaveCountingCanvas : public SkCanvas {
543public:
544 SaveCountingCanvas(int width, int height)
545 : INHERITED(width, height)
546 , fSaveCount(0)
547 , fSaveLayerCount(0)
548 , fRestoreCount(0){
549 }
550
551 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
mtklein36352bf2015-03-25 18:17:31 -0700552 SaveFlags flags) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000553 ++fSaveLayerCount;
554 return this->INHERITED::willSaveLayer(bounds, paint, flags);
555 }
556
mtklein36352bf2015-03-25 18:17:31 -0700557 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000558 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400559 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000560 }
561
mtklein36352bf2015-03-25 18:17:31 -0700562 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000563 ++fRestoreCount;
564 this->INHERITED::willRestore();
565 }
566
567 unsigned int getSaveCount() const { return fSaveCount; }
568 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
569 unsigned int getRestoreCount() const { return fRestoreCount; }
570
571private:
572 unsigned int fSaveCount;
573 unsigned int fSaveLayerCount;
574 unsigned int fRestoreCount;
575
576 typedef SkCanvas INHERITED;
577};
578
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000579void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000580 unsigned int numSaves, unsigned int numSaveLayers,
581 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700582 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700583 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000584
robertphillipsc5ba71d2014-09-04 08:42:50 -0700585 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000586
mtklein87c41382014-09-08 07:31:18 -0700587 // Optimizations may have removed these,
588 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
589 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
590 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
591 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000592}
593
594// This class exists so SkPicture can friend it and give it access to
595// the 'partialReplay' method.
596class SkPictureRecorderReplayTester {
597public:
598 static SkPicture* Copy(SkPictureRecorder* recorder) {
599 SkPictureRecorder recorder2;
600
robertphillips9f1c2412014-06-09 06:25:34 -0700601 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000602
603 recorder->partialReplay(canvas);
604
605 return recorder2.endRecording();
606 }
607};
608
robertphillips9058d602014-06-10 11:45:46 -0700609static void create_imbalance(SkCanvas* canvas) {
610 SkRect clipRect = SkRect::MakeWH(2, 2);
611 SkRect drawRect = SkRect::MakeWH(10, 10);
612 canvas->save();
613 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
614 canvas->translate(1.0f, 1.0f);
615 SkPaint p;
616 p.setColor(SK_ColorGREEN);
617 canvas->drawRect(drawRect, p);
618 // no restore
619}
620
621// This tests that replaying a potentially unbalanced picture into a canvas
622// doesn't affect the canvas' save count or matrix/clip state.
623static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
624 SkBitmap bm;
625 bm.allocN32Pixels(4, 3);
626 SkCanvas canvas(bm);
627
628 int beforeSaveCount = canvas.getSaveCount();
629
630 SkMatrix beforeMatrix = canvas.getTotalMatrix();
631
632 SkRect beforeClip;
633
634 canvas.getClipBounds(&beforeClip);
635
636 canvas.drawPicture(picture);
637
638 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
639 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
640
641 SkRect afterClip;
642
643 canvas.getClipBounds(&afterClip);
644
645 REPORTER_ASSERT(reporter, afterClip == beforeClip);
646}
647
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000648// Test out SkPictureRecorder::partialReplay
649DEF_TEST(PictureRecorder_replay, reporter) {
650 // check save/saveLayer state
651 {
652 SkPictureRecorder recorder;
653
robertphillips9f1c2412014-06-09 06:25:34 -0700654 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000655
656 canvas->saveLayer(NULL, NULL);
657
658 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
659
660 // The extra save and restore comes from the Copy process.
661 check_save_state(reporter, copy, 2, 1, 3);
662
663 canvas->saveLayer(NULL, NULL);
664
665 SkAutoTUnref<SkPicture> final(recorder.endRecording());
666
667 check_save_state(reporter, final, 1, 2, 3);
668
669 // The copy shouldn't pick up any operations added after it was made
670 check_save_state(reporter, copy, 2, 1, 3);
671 }
672
673 // (partially) check leakage of draw ops
674 {
675 SkPictureRecorder recorder;
676
robertphillips9f1c2412014-06-09 06:25:34 -0700677 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000678
679 SkRect r = SkRect::MakeWH(5, 5);
680 SkPaint p;
681
682 canvas->drawRect(r, p);
683
684 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
685
686 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
687
688 SkBitmap bm;
689 make_bm(&bm, 10, 10, SK_ColorRED, true);
690
691 r.offset(5.0f, 5.0f);
reede47829b2015-08-06 10:02:53 -0700692 canvas->drawBitmapRect(bm, r, nullptr);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000693
694 SkAutoTUnref<SkPicture> final(recorder.endRecording());
695 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
696
697 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
698
699 // The snapshot shouldn't pick up any operations added after it was made
700 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
701 }
robertphillips9058d602014-06-10 11:45:46 -0700702
703 // Recreate the Android partialReplay test case
704 {
705 SkPictureRecorder recorder;
706
707 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
708 create_imbalance(canvas);
709
710 int expectedSaveCount = canvas->getSaveCount();
711
712 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
713 check_balance(reporter, copy);
714
715 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
716
717 // End the recording of source to test the picture finalization
718 // process isn't complicated by the partialReplay step
719 SkAutoTUnref<SkPicture> final(recorder.endRecording());
720 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000721}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000722
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000723static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
724 SkCanvas testCanvas(100, 100);
725 set_canvas_to_save_count_4(&testCanvas);
726
727 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
728
729 SkPaint paint;
730 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
731
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000732 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000733
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000734 {
735 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700736 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000737 canvas->save();
738 canvas->translate(10, 10);
739 canvas->drawRect(rect, paint);
740 canvas->save();
741 canvas->translate(10, 10);
742 canvas->drawRect(rect, paint);
743 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
744
robertphillips9b14f262014-06-04 05:40:44 -0700745 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000746 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
747 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000748
749 set_canvas_to_save_count_4(&testCanvas);
750
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000751 {
752 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700753 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000754 canvas->save();
755 canvas->translate(10, 10);
756 canvas->drawRect(rect, paint);
757 canvas->save();
758 canvas->translate(10, 10);
759 canvas->drawRect(rect, paint);
760 canvas->restore();
761 canvas->restore();
762 canvas->restore();
763 canvas->restore();
764 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000765
robertphillips9b14f262014-06-04 05:40:44 -0700766 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000767 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
768 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000769
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000770 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000771
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000772 {
robertphillips9f1c2412014-06-09 06:25:34 -0700773 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000774 canvas->translate(10, 10);
775 canvas->drawRect(rect, paint);
776 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
777
robertphillips9b14f262014-06-04 05:40:44 -0700778 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000779 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
780 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
781 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000782}
783
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000784static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000785 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000786
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000787 SkPictureRecorder recorder;
788
reed@google.com21b519d2012-10-02 17:42:15 +0000789 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000790 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000791
robertphillips9f1c2412014-06-09 06:25:34 -0700792 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000793
794 for (int i = 0; i < 1000; ++i) {
795 rand_op(canvas, rand);
796 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000797 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000798
799 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000800 }
801
802 {
robertphillips9f1c2412014-06-09 06:25:34 -0700803 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000804 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000805
reed@google.com21b519d2012-10-02 17:42:15 +0000806 for (int i = 0; i < 100; ++i) {
807 canvas->save();
808 }
809 while (canvas->getSaveCount() > 1) {
810 canvas->clipRect(rect);
811 canvas->restore();
812 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000813 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000814 }
815}
816
scroggo@google.com4b90b112012-12-04 15:08:56 +0000817#ifndef SK_DEBUG
818// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
819// should never do this.
820static void test_bad_bitmap() {
821 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
822 // fail.
823 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000824 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000825 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700826 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000827 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000828 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000829
830 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700831 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000832}
833#endif
834
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000835static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000836 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700837 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700838 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000839 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000840 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
841
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000842 SkDynamicMemoryWStream wStream;
robertphillips3e5c2b12015-03-23 05:46:51 -0700843 sk_tool_utils::PngPixelSerializer serializer;
scroggo895c43b2014-12-11 10:53:58 -0800844 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000845 return wStream.copyToData();
846}
847
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000848struct ErrorContext {
849 int fErrors;
850 skiatest::Reporter* fReporter;
851};
852
853static void assert_one_parse_error_cb(SkError error, void* context) {
854 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
855 errorContext->fErrors++;
856 // This test only expects one error, and that is a kParseError. If there are others,
857 // there is some unknown problem.
858 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
859 "This threw more errors than expected.");
860 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
861 SkGetLastErrorString());
862}
863
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000864static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
865 // Create a bitmap that will be encoded.
866 SkBitmap original;
867 make_bm(&original, 100, 100, SK_ColorBLUE, true);
868 SkDynamicMemoryWStream wStream;
869 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
870 return;
871 }
872 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000873
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000874 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800875 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000876 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000877
878 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
879 // Flattening original will follow the old path of performing an encode, while flattening bm
880 // will use the already encoded data.
881 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
882 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
883 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000884 // Now test that a parse error was generated when trying to create a new SkPicture without
885 // providing a function to decode the bitmap.
886 ErrorContext context;
887 context.fErrors = 0;
888 context.fReporter = reporter;
889 SkSetErrorCallback(assert_one_parse_error_cb, &context);
890 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000891 SkClearLastError();
mtklein08d1fcc2014-11-20 09:18:31 -0800892 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000893 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000894 SkClearLastError();
895 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000896}
897
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000898static void test_clip_bound_opt(skiatest::Reporter* reporter) {
899 // Test for crbug.com/229011
900 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
901 SkIntToScalar(2), SkIntToScalar(2));
902 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
903 SkIntToScalar(1), SkIntToScalar(1));
904 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
905 SkIntToScalar(1), SkIntToScalar(1));
906
907 SkPath invPath;
908 invPath.addOval(rect1);
909 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
910 SkPath path;
911 path.addOval(rect2);
912 SkPath path2;
913 path2.addOval(rect3);
914 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000915 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700916
917 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000918 {
robertphillips9f1c2412014-06-09 06:25:34 -0700919 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000920 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
921 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
922 REPORTER_ASSERT(reporter, true == nonEmpty);
923 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
924 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
925 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
926 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
927 }
928 {
robertphillips9f1c2412014-06-09 06:25:34 -0700929 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000930 canvas->clipPath(path, SkRegion::kIntersect_Op);
931 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
932 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
933 REPORTER_ASSERT(reporter, true == nonEmpty);
934 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
935 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
936 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
937 REPORTER_ASSERT(reporter, 8 == 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::kUnion_Op);
943 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
944 REPORTER_ASSERT(reporter, true == nonEmpty);
945 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
946 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
947 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
948 REPORTER_ASSERT(reporter, 10 == 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::kDifference_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::kReverseDifference_Op);
963 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
964 // True clip is actually empty in this case, but the best
965 // determination we can make using only bounds as input is that the
966 // clip is included in the bounds of 'path'.
967 REPORTER_ASSERT(reporter, true == nonEmpty);
968 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
969 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
970 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
971 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
972 }
973 {
robertphillips9f1c2412014-06-09 06:25:34 -0700974 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000975 canvas->clipPath(path, SkRegion::kIntersect_Op);
976 canvas->clipPath(path2, SkRegion::kXOR_Op);
977 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
978 REPORTER_ASSERT(reporter, true == nonEmpty);
979 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
980 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
982 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
983 }
984}
985
schenneyeeff8bb2015-07-07 14:27:10 -0700986static void test_cull_rect_reset(skiatest::Reporter* reporter) {
987 SkPictureRecorder recorder;
988 SkRect bounds = SkRect::MakeWH(10, 10);
989 SkRTreeFactory factory;
990 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
991 bounds = SkRect::MakeWH(100, 100);
992 SkPaint paint;
993 canvas->drawRect(bounds, paint);
994 canvas->drawRect(bounds, paint);
mtkleineedc3342015-07-08 08:26:39 -0700995 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
996 const SkBigPicture* picture = p->asSkBigPicture();
schenneyeeff8bb2015-07-07 14:27:10 -0700997 REPORTER_ASSERT(reporter, picture);
998
999 SkRect finalCullRect = picture->cullRect();
1000 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1001 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1002 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1003 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1004
1005 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1006 SkRect bbhCullRect = pictureBBH->getRootBound();
1007 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1008 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1009 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1010 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1011}
1012
1013
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001014/**
1015 * A canvas that records the number of clip commands.
1016 */
1017class ClipCountingCanvas : public SkCanvas {
1018public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001019 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001020 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001021 , fClipCount(0){
1022 }
1023
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001024 virtual void onClipRect(const SkRect& r,
1025 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001026 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001027 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001028 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001029 }
1030
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001031 virtual void onClipRRect(const SkRRect& rrect,
1032 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001033 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001034 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001035 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001036 }
1037
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001038 virtual void onClipPath(const SkPath& path,
1039 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001040 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001041 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001042 this->INHERITED::onClipPath(path, op, edgeStyle);
1043 }
1044
mtklein36352bf2015-03-25 18:17:31 -07001045 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001046 fClipCount += 1;
1047 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001048 }
1049
1050 unsigned getClipCount() const { return fClipCount; }
1051
1052private:
1053 unsigned fClipCount;
1054
1055 typedef SkCanvas INHERITED;
1056};
1057
1058static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001059 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001060 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001061
1062 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1063 // The following expanding clip should not be skipped.
1064 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1065 // Draw something so the optimizer doesn't just fold the world.
1066 SkPaint p;
1067 p.setColor(SK_ColorBLUE);
1068 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001069 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001070
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001071 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001072 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001073
1074 // Both clips should be present on playback.
1075 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1076}
1077
tomhudson@google.com381010e2013-10-24 11:12:47 +00001078static void test_hierarchical(skiatest::Reporter* reporter) {
1079 SkBitmap bm;
1080 make_bm(&bm, 10, 10, SK_ColorRED, true);
1081
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001082 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001083
robertphillips9f1c2412014-06-09 06:25:34 -07001084 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001085 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1086 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001087
robertphillips9f1c2412014-06-09 06:25:34 -07001088 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001089 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1090 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001091
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001092 {
robertphillips9f1c2412014-06-09 06:25:34 -07001093 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001094 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001095 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1096 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1097 }
1098 {
robertphillips9f1c2412014-06-09 06:25:34 -07001099 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001100 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001101 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1102 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1103 }
1104 {
robertphillips9f1c2412014-06-09 06:25:34 -07001105 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001106 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001107 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001108 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1109 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1110 }
1111 {
robertphillips9f1c2412014-06-09 06:25:34 -07001112 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001113 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001114 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001115 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1116 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1117 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001118}
1119
robertphillips@google.comd5500882014-04-02 23:51:13 +00001120static void test_gen_id(skiatest::Reporter* reporter) {
1121
Robert Phillipscfaeec42014-07-13 12:00:50 -04001122 SkPictureRecorder recorder;
1123 recorder.beginRecording(0, 0);
1124 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001125
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001126 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001127 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001128
robertphillips9f1c2412014-06-09 06:25:34 -07001129 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001130 canvas->drawARGB(255, 255, 255, 255);
1131 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1132 // picture should have a non-zero id after recording
1133 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001134
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001135 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001136 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001137}
1138
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001139DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001140#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001141 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001142 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001143#else
1144 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001145#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001146 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001147 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001148#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001149 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001150#endif
mtklein8e126562014-10-01 09:29:35 -07001151 test_has_text(reporter);
1152 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001153 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001154 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001155 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001156 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001157 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001158 test_savelayer_extraction(reporter);
schenneyeeff8bb2015-07-07 14:27:10 -07001159 test_cull_rect_reset(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001160}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001161
1162static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1163 const SkPaint paint;
1164 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1165 const SkIRect irect = { 2, 2, 3, 3 };
1166
1167 // Don't care what these record, as long as they're legal.
1168 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
reede47829b2015-08-06 10:02:53 -07001169 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001170 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1171 canvas->drawSprite(bitmap, 1, 1);
1172}
1173
1174static void test_draw_bitmaps(SkCanvas* canvas) {
1175 SkBitmap empty;
1176 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001177 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001178 draw_bitmaps(empty, canvas);
1179}
1180
1181DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001182 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001183 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001184 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001185}
1186
1187DEF_TEST(Canvas_EmptyBitmap, r) {
1188 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001189 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001190 SkCanvas canvas(dst);
1191
1192 test_draw_bitmaps(&canvas);
1193}
dneto3f22e8c2014-07-30 15:42:22 -07001194
1195DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1196 // This test is from crbug.com/344987.
1197 // The commands are:
1198 // saveLayer with paint that modifies alpha
reed84984ef2015-07-17 07:09:43 -07001199 // drawBitmapRect
1200 // drawBitmapRect
dneto3f22e8c2014-07-30 15:42:22 -07001201 // restore
1202 // The bug was that this structure was modified so that:
1203 // - The saveLayer and restore were eliminated
1204 // - The alpha was only applied to the first drawBitmapRectToRect
1205
1206 // This test draws blue and red squares inside a 50% transparent
1207 // layer. Both colours should show up muted.
1208 // When the bug is present, the red square (the second bitmap)
1209 // shows upwith full opacity.
1210
1211 SkBitmap blueBM;
1212 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1213 SkBitmap redBM;
1214 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1215 SkPaint semiTransparent;
1216 semiTransparent.setAlpha(0x80);
1217
1218 SkPictureRecorder recorder;
1219 SkCanvas* canvas = recorder.beginRecording(100, 100);
1220 canvas->drawARGB(0, 0, 0, 0);
1221
1222 canvas->saveLayer(0, &semiTransparent);
1223 canvas->drawBitmap(blueBM, 25, 25);
1224 canvas->drawBitmap(redBM, 50, 50);
1225 canvas->restore();
1226
1227 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1228
1229 // Now replay the picture back on another canvas
1230 // and check a couple of its pixels.
1231 SkBitmap replayBM;
1232 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1233 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001234 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001235 replayCanvas.flush();
1236
1237 // With the bug present, at (55, 55) we would get a fully opaque red
1238 // intead of a dark red.
1239 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1240 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1241}
mtklein3e8232b2014-08-18 13:39:11 -07001242
1243struct CountingBBH : public SkBBoxHierarchy {
1244 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001245 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001246
schenney23d85932015-03-06 16:20:28 -08001247 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001248
mtkleinc6ad06a2015-08-19 09:51:00 -07001249 void search(const SkRect& query, SkTDArray<int>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001250 this->searchCalls++;
1251 }
1252
mtklein36352bf2015-03-25 18:17:31 -07001253 void insert(const SkRect[], int) override {}
1254 virtual size_t bytesUsed() const override { return 0; }
1255 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001256};
1257
1258class SpoonFedBBHFactory : public SkBBHFactory {
1259public:
1260 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001261 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001262 return SkRef(fBBH);
1263 }
1264private:
1265 SkBBoxHierarchy* fBBH;
1266};
1267
1268// When the canvas clip covers the full picture, we don't need to call the BBH.
1269DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001270 SkRect bound = SkRect::MakeWH(320, 240);
1271 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001272 SpoonFedBBHFactory factory(&bbh);
1273
1274 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001275 SkCanvas* c = recorder.beginRecording(bound, &factory);
1276 // Record a few ops so we don't hit a small- or empty- picture optimization.
1277 c->drawRect(bound, SkPaint());
1278 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001279 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1280
1281 SkCanvas big(640, 480), small(300, 200);
1282
robertphillipsc5ba71d2014-09-04 08:42:50 -07001283 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001284 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1285
robertphillipsc5ba71d2014-09-04 08:42:50 -07001286 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001287 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1288}
mtkleind72094d2014-08-27 12:12:23 -07001289
1290DEF_TEST(Picture_BitmapLeak, r) {
1291 SkBitmap mut, immut;
1292 mut.allocN32Pixels(300, 200);
1293 immut.allocN32Pixels(300, 200);
1294 immut.setImmutable();
1295 SkASSERT(!mut.isImmutable());
1296 SkASSERT(immut.isImmutable());
1297
1298 // No one can hold a ref on our pixels yet.
1299 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1300 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1301
reed1bdfd3f2014-11-24 14:41:51 -08001302 SkAutoTUnref<const SkPicture> pic;
1303 {
1304 // we want the recorder to go out of scope before our subsequent checks, so we
1305 // place it inside local braces.
1306 SkPictureRecorder rec;
1307 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1308 canvas->drawBitmap(mut, 0, 0);
1309 canvas->drawBitmap(immut, 800, 600);
1310 pic.reset(rec.endRecording());
1311 }
mtkleind72094d2014-08-27 12:12:23 -07001312
1313 // The picture shares the immutable pixels but copies the mutable ones.
1314 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1315 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1316
1317 // When the picture goes away, it's just our bitmaps holding the refs.
1318 pic.reset(NULL);
1319 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1320 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1321}
mtkleinfeaadee2015-04-08 11:25:48 -07001322
1323// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1324DEF_TEST(Picture_getRecordingCanvas, r) {
1325 SkPictureRecorder rec;
1326 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1327 for (int i = 0; i < 3; i++) {
1328 rec.beginRecording(100, 100);
1329 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1330 rec.endRecording()->unref();
1331 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1332 }
1333}
mtklein9db912c2015-05-19 11:11:26 -07001334
1335DEF_TEST(MiniRecorderLeftHanging, r) {
1336 // Any shader or other ref-counted effect will do just fine here.
1337 SkPaint paint;
1338 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1339
1340 SkMiniRecorder rec;
1341 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1342 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1343}
fmalita2ecc0002015-07-14 13:12:25 -07001344
1345DEF_TEST(Picture_preserveCullRect, r) {
1346 SkPictureRecorder recorder;
1347
1348 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1349 c->clear(SK_ColorCYAN);
1350
1351 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1352 SkDynamicMemoryWStream wstream;
1353 picture->serialize(&wstream);
1354
1355 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1356 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1357
1358 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1359 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1360 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1361 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1362 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1363}