blob: 2fe6e47ab548e9edecc384aa9333adc24b149b2a [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"
reed@google.com72aa79c2013-01-24 18:27:42 +000026#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000027#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080028#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000029#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000030#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070031#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000032
33#if SK_SUPPORT_GPU
34#include "SkSurface.h"
35#include "GrContextFactory.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000036#endif
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000037#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000038
reed@google.com47b679b2014-05-14 18:58:16 +000039#include "SkLumaColorFilter.h"
40#include "SkColorFilterImageFilter.h"
41
reed@google.comfe7b1ed2012-11-29 21:00:39 +000042static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000043 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000044 bm->eraseColor(color);
45 if (immutable) {
46 bm->setImmutable();
47 }
48}
49
tomhudson3a0f2792014-08-20 05:29:41 -070050/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070051static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070052 SkPictureRecorder recorder;
53
mtklein8e126562014-10-01 09:29:35 -070054 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070055 {
56 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
57 }
58 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
59 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
60
mtklein8e126562014-10-01 09:29:35 -070061 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070062 {
63 SkPaint paint;
64 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
65 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
66 SkBitmap bitmap;
67 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
68 bitmap.eraseColor(SK_ColorBLUE);
69 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
70 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
71 SkShader::kClamp_TileMode);
72 paint.setShader(shader)->unref();
73 REPORTER_ASSERT(reporter,
74 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
75
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()) {
robertphillips82365912014-11-12 09:32:34 -0800366 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000367
robertphillips82365912014-11-12 09:32:34 -0800368 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
369 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000370
robertphillips82365912014-11-12 09:32:34 -0800371 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
372 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700373
robertphillips82365912014-11-12 09:32:34 -0800374 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
375 // The parent/child layers appear in reverse order
376 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
377 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700378
robertphillips82365912014-11-12 09:32:34 -0800379 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700380
robertphillips82365912014-11-12 09:32:34 -0800381 // The parent/child layers appear in reverse order
382 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
383 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000384
robertphillips82365912014-11-12 09:32:34 -0800385 // The parent/child layers appear in reverse order
386 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
387 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000388
robertphillips82365912014-11-12 09:32:34 -0800389 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
390 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
391 kHeight == info0.fBounds.height());
392 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
393 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
394 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
395 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
396 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000397
robertphillips82365912014-11-12 09:32:34 -0800398 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
399 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
400 kHeight/2.0 == info1.fBounds.height());
401 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
402 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800403 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800404 kHeight/2.0 == info1.fBounds.fTop);
405 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
406 REPORTER_ASSERT(reporter, !info1.fIsNested &&
407 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000408
robertphillips82365912014-11-12 09:32:34 -0800409 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
410 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
411 kHeight / 2 == info2.fBounds.height()); // bound reduces size
412 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
413 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
414 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
415 kHeight / 2 == info2.fBounds.fTop);
416 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
417 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000418
robertphillips82365912014-11-12 09:32:34 -0800419 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
420 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
421 kHeight == info3.fBounds.height());
422 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
423 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
424 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
425 REPORTER_ASSERT(reporter, info3.fPaint);
426 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700427
robertphillips82365912014-11-12 09:32:34 -0800428 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
429 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
430 kHeight == info4.fBounds.height());
431 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
432 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
433 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
434 REPORTER_ASSERT(reporter, info4.fPaint);
435 REPORTER_ASSERT(reporter, !info4.fIsNested &&
436 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700437
robertphillips82365912014-11-12 09:32:34 -0800438 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
439 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
440 kHeight == info5.fBounds.height());
441 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
442 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
443 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
444 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
445 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700446
robertphillips82365912014-11-12 09:32:34 -0800447 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
448 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
449 kHeight-10 == info6.fBounds.height());
450 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
451 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
452 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
453 REPORTER_ASSERT(reporter, info6.fPaint);
454 REPORTER_ASSERT(reporter, !info6.fIsNested &&
455 info6.fHasNestedLayers); // has a nested SL
456
457 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
458 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
459 kHeight == info7.fBounds.height());
460 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
461 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
462 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
463 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
464 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000465 }
466}
467
mtklein8e126562014-10-01 09:29:35 -0700468static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700469 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700470
mtklein8e126562014-10-01 09:29:35 -0700471 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700472 {
mtkleinc551d9f2014-08-20 08:09:46 -0700473 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700474 }
475 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
476 REPORTER_ASSERT(reporter, !picture->hasText());
477
mtkleinc551d9f2014-08-20 08:09:46 -0700478 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700479 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700480 {
mtkleinc551d9f2014-08-20 08:09:46 -0700481 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700482 }
483 picture.reset(recorder.endRecording());
484 REPORTER_ASSERT(reporter, picture->hasText());
485
mtklein8e126562014-10-01 09:29:35 -0700486 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700487 {
mtkleinc551d9f2014-08-20 08:09:46 -0700488 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700489 }
490 picture.reset(recorder.endRecording());
491 REPORTER_ASSERT(reporter, picture->hasText());
492
mtklein8e126562014-10-01 09:29:35 -0700493 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700494 {
mtkleinc551d9f2014-08-20 08:09:46 -0700495 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700496 }
497 picture.reset(recorder.endRecording());
498 REPORTER_ASSERT(reporter, picture->hasText());
499
mtklein8e126562014-10-01 09:29:35 -0700500 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700501 {
502 SkPath path;
503 path.moveTo(0, 0);
504 path.lineTo(50, 50);
505
mtkleinc551d9f2014-08-20 08:09:46 -0700506 canvas->drawTextOnPathHV("Q", 1, path, 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->drawTextOnPath("Q", 1, path, NULL, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700518 }
519 picture.reset(recorder.endRecording());
520 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700521
522 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700523 canvas = recorder.beginRecording(100,100);
524 {
525 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700526 }
mtklein8e126562014-10-01 09:29:35 -0700527 picture.reset(recorder.endRecording());
528 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700529}
530
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000531static void set_canvas_to_save_count_4(SkCanvas* canvas) {
532 canvas->restoreToCount(1);
533 canvas->save();
534 canvas->save();
535 canvas->save();
536}
537
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000538/**
539 * A canvas that records the number of saves, saveLayers and restores.
540 */
541class SaveCountingCanvas : public SkCanvas {
542public:
543 SaveCountingCanvas(int width, int height)
544 : INHERITED(width, height)
545 , fSaveCount(0)
546 , fSaveLayerCount(0)
547 , fRestoreCount(0){
548 }
549
550 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
mtklein36352bf2015-03-25 18:17:31 -0700551 SaveFlags flags) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000552 ++fSaveLayerCount;
553 return this->INHERITED::willSaveLayer(bounds, paint, flags);
554 }
555
mtklein36352bf2015-03-25 18:17:31 -0700556 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000557 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400558 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000559 }
560
mtklein36352bf2015-03-25 18:17:31 -0700561 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000562 ++fRestoreCount;
563 this->INHERITED::willRestore();
564 }
565
566 unsigned int getSaveCount() const { return fSaveCount; }
567 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
568 unsigned int getRestoreCount() const { return fRestoreCount; }
569
570private:
571 unsigned int fSaveCount;
572 unsigned int fSaveLayerCount;
573 unsigned int fRestoreCount;
574
575 typedef SkCanvas INHERITED;
576};
577
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000578void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000579 unsigned int numSaves, unsigned int numSaveLayers,
580 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700581 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700582 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000583
robertphillipsc5ba71d2014-09-04 08:42:50 -0700584 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000585
mtklein87c41382014-09-08 07:31:18 -0700586 // Optimizations may have removed these,
587 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
588 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
589 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
590 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000591}
592
593// This class exists so SkPicture can friend it and give it access to
594// the 'partialReplay' method.
595class SkPictureRecorderReplayTester {
596public:
597 static SkPicture* Copy(SkPictureRecorder* recorder) {
598 SkPictureRecorder recorder2;
599
robertphillips9f1c2412014-06-09 06:25:34 -0700600 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000601
602 recorder->partialReplay(canvas);
603
604 return recorder2.endRecording();
605 }
606};
607
robertphillips9058d602014-06-10 11:45:46 -0700608static void create_imbalance(SkCanvas* canvas) {
609 SkRect clipRect = SkRect::MakeWH(2, 2);
610 SkRect drawRect = SkRect::MakeWH(10, 10);
611 canvas->save();
612 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
613 canvas->translate(1.0f, 1.0f);
614 SkPaint p;
615 p.setColor(SK_ColorGREEN);
616 canvas->drawRect(drawRect, p);
617 // no restore
618}
619
620// This tests that replaying a potentially unbalanced picture into a canvas
621// doesn't affect the canvas' save count or matrix/clip state.
622static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
623 SkBitmap bm;
624 bm.allocN32Pixels(4, 3);
625 SkCanvas canvas(bm);
626
627 int beforeSaveCount = canvas.getSaveCount();
628
629 SkMatrix beforeMatrix = canvas.getTotalMatrix();
630
631 SkRect beforeClip;
632
633 canvas.getClipBounds(&beforeClip);
634
635 canvas.drawPicture(picture);
636
637 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
638 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
639
640 SkRect afterClip;
641
642 canvas.getClipBounds(&afterClip);
643
644 REPORTER_ASSERT(reporter, afterClip == beforeClip);
645}
646
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000647// Test out SkPictureRecorder::partialReplay
648DEF_TEST(PictureRecorder_replay, reporter) {
649 // check save/saveLayer state
650 {
651 SkPictureRecorder recorder;
652
robertphillips9f1c2412014-06-09 06:25:34 -0700653 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000654
655 canvas->saveLayer(NULL, NULL);
656
657 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
658
659 // The extra save and restore comes from the Copy process.
660 check_save_state(reporter, copy, 2, 1, 3);
661
662 canvas->saveLayer(NULL, NULL);
663
664 SkAutoTUnref<SkPicture> final(recorder.endRecording());
665
666 check_save_state(reporter, final, 1, 2, 3);
667
668 // The copy shouldn't pick up any operations added after it was made
669 check_save_state(reporter, copy, 2, 1, 3);
670 }
671
672 // (partially) check leakage of draw ops
673 {
674 SkPictureRecorder recorder;
675
robertphillips9f1c2412014-06-09 06:25:34 -0700676 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000677
678 SkRect r = SkRect::MakeWH(5, 5);
679 SkPaint p;
680
681 canvas->drawRect(r, p);
682
683 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
684
685 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
686
687 SkBitmap bm;
688 make_bm(&bm, 10, 10, SK_ColorRED, true);
689
690 r.offset(5.0f, 5.0f);
691 canvas->drawBitmapRectToRect(bm, NULL, r);
692
693 SkAutoTUnref<SkPicture> final(recorder.endRecording());
694 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
695
696 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
697
698 // The snapshot shouldn't pick up any operations added after it was made
699 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
700 }
robertphillips9058d602014-06-10 11:45:46 -0700701
702 // Recreate the Android partialReplay test case
703 {
704 SkPictureRecorder recorder;
705
706 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
707 create_imbalance(canvas);
708
709 int expectedSaveCount = canvas->getSaveCount();
710
711 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
712 check_balance(reporter, copy);
713
714 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
715
716 // End the recording of source to test the picture finalization
717 // process isn't complicated by the partialReplay step
718 SkAutoTUnref<SkPicture> final(recorder.endRecording());
719 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000720}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000721
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000722static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
723 SkCanvas testCanvas(100, 100);
724 set_canvas_to_save_count_4(&testCanvas);
725
726 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
727
728 SkPaint paint;
729 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
730
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000731 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000732
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000733 {
734 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700735 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000736 canvas->save();
737 canvas->translate(10, 10);
738 canvas->drawRect(rect, paint);
739 canvas->save();
740 canvas->translate(10, 10);
741 canvas->drawRect(rect, paint);
742 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
743
robertphillips9b14f262014-06-04 05:40:44 -0700744 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000745 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
746 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000747
748 set_canvas_to_save_count_4(&testCanvas);
749
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000750 {
751 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700752 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000753 canvas->save();
754 canvas->translate(10, 10);
755 canvas->drawRect(rect, paint);
756 canvas->save();
757 canvas->translate(10, 10);
758 canvas->drawRect(rect, paint);
759 canvas->restore();
760 canvas->restore();
761 canvas->restore();
762 canvas->restore();
763 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000764
robertphillips9b14f262014-06-04 05:40:44 -0700765 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000766 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
767 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000768
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000769 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000770
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000771 {
robertphillips9f1c2412014-06-09 06:25:34 -0700772 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000773 canvas->translate(10, 10);
774 canvas->drawRect(rect, paint);
775 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
776
robertphillips9b14f262014-06-04 05:40:44 -0700777 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000778 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
779 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
780 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000781}
782
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000783static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000784 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000785
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000786 SkPictureRecorder recorder;
787
reed@google.com21b519d2012-10-02 17:42:15 +0000788 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000789 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000790
robertphillips9f1c2412014-06-09 06:25:34 -0700791 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000792
793 for (int i = 0; i < 1000; ++i) {
794 rand_op(canvas, rand);
795 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000796 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000797
798 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000799 }
800
801 {
robertphillips9f1c2412014-06-09 06:25:34 -0700802 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000803 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000804
reed@google.com21b519d2012-10-02 17:42:15 +0000805 for (int i = 0; i < 100; ++i) {
806 canvas->save();
807 }
808 while (canvas->getSaveCount() > 1) {
809 canvas->clipRect(rect);
810 canvas->restore();
811 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000812 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000813 }
814}
815
scroggo@google.com4b90b112012-12-04 15:08:56 +0000816#ifndef SK_DEBUG
817// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
818// should never do this.
819static void test_bad_bitmap() {
820 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
821 // fail.
822 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000823 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000824 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700825 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000826 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000827 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000828
829 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700830 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000831}
832#endif
833
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000834static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000835 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700836 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700837 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000838 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000839 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
840
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000841 SkDynamicMemoryWStream wStream;
robertphillips3e5c2b12015-03-23 05:46:51 -0700842 sk_tool_utils::PngPixelSerializer serializer;
scroggo895c43b2014-12-11 10:53:58 -0800843 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000844 return wStream.copyToData();
845}
846
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000847struct ErrorContext {
848 int fErrors;
849 skiatest::Reporter* fReporter;
850};
851
852static void assert_one_parse_error_cb(SkError error, void* context) {
853 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
854 errorContext->fErrors++;
855 // This test only expects one error, and that is a kParseError. If there are others,
856 // there is some unknown problem.
857 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
858 "This threw more errors than expected.");
859 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
860 SkGetLastErrorString());
861}
862
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000863static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
864 // Create a bitmap that will be encoded.
865 SkBitmap original;
866 make_bm(&original, 100, 100, SK_ColorBLUE, true);
867 SkDynamicMemoryWStream wStream;
868 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
869 return;
870 }
871 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000872
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000873 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800874 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000875 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000876
877 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
878 // Flattening original will follow the old path of performing an encode, while flattening bm
879 // will use the already encoded data.
880 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
881 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
882 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000883 // Now test that a parse error was generated when trying to create a new SkPicture without
884 // providing a function to decode the bitmap.
885 ErrorContext context;
886 context.fErrors = 0;
887 context.fReporter = reporter;
888 SkSetErrorCallback(assert_one_parse_error_cb, &context);
889 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000890 SkClearLastError();
mtklein08d1fcc2014-11-20 09:18:31 -0800891 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000892 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000893 SkClearLastError();
894 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000895}
896
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000897static void test_clip_bound_opt(skiatest::Reporter* reporter) {
898 // Test for crbug.com/229011
899 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
900 SkIntToScalar(2), SkIntToScalar(2));
901 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
902 SkIntToScalar(1), SkIntToScalar(1));
903 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
904 SkIntToScalar(1), SkIntToScalar(1));
905
906 SkPath invPath;
907 invPath.addOval(rect1);
908 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
909 SkPath path;
910 path.addOval(rect2);
911 SkPath path2;
912 path2.addOval(rect3);
913 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000914 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700915
916 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000917 {
robertphillips9f1c2412014-06-09 06:25:34 -0700918 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000919 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
920 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
921 REPORTER_ASSERT(reporter, true == nonEmpty);
922 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
923 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
924 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
925 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
926 }
927 {
robertphillips9f1c2412014-06-09 06:25:34 -0700928 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000929 canvas->clipPath(path, SkRegion::kIntersect_Op);
930 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
931 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
932 REPORTER_ASSERT(reporter, true == nonEmpty);
933 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
934 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
935 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
936 REPORTER_ASSERT(reporter, 8 == 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::kUnion_Op);
942 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
943 REPORTER_ASSERT(reporter, true == nonEmpty);
944 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
945 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
946 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
947 REPORTER_ASSERT(reporter, 10 == 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::kDifference_Op);
952 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
953 REPORTER_ASSERT(reporter, true == nonEmpty);
954 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
955 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
956 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
957 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
958 }
959 {
robertphillips9f1c2412014-06-09 06:25:34 -0700960 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000961 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
962 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
963 // True clip is actually empty in this case, but the best
964 // determination we can make using only bounds as input is that the
965 // clip is included in the bounds of 'path'.
966 REPORTER_ASSERT(reporter, true == nonEmpty);
967 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
968 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
969 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
970 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
971 }
972 {
robertphillips9f1c2412014-06-09 06:25:34 -0700973 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000974 canvas->clipPath(path, SkRegion::kIntersect_Op);
975 canvas->clipPath(path2, SkRegion::kXOR_Op);
976 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
977 REPORTER_ASSERT(reporter, true == nonEmpty);
978 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
979 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
980 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
982 }
983}
984
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000985/**
986 * A canvas that records the number of clip commands.
987 */
988class ClipCountingCanvas : public SkCanvas {
989public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000990 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000991 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000992 , fClipCount(0){
993 }
994
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000995 virtual void onClipRect(const SkRect& r,
996 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -0700997 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000998 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +0000999 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001000 }
1001
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001002 virtual void onClipRRect(const SkRRect& rrect,
1003 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001004 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001005 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001006 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001007 }
1008
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001009 virtual void onClipPath(const SkPath& path,
1010 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001011 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001012 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001013 this->INHERITED::onClipPath(path, op, edgeStyle);
1014 }
1015
mtklein36352bf2015-03-25 18:17:31 -07001016 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001017 fClipCount += 1;
1018 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001019 }
1020
1021 unsigned getClipCount() const { return fClipCount; }
1022
1023private:
1024 unsigned fClipCount;
1025
1026 typedef SkCanvas INHERITED;
1027};
1028
1029static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001030 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001031 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001032
1033 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1034 // The following expanding clip should not be skipped.
1035 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1036 // Draw something so the optimizer doesn't just fold the world.
1037 SkPaint p;
1038 p.setColor(SK_ColorBLUE);
1039 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001040 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001041
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001042 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001043 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001044
1045 // Both clips should be present on playback.
1046 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1047}
1048
tomhudson@google.com381010e2013-10-24 11:12:47 +00001049static void test_hierarchical(skiatest::Reporter* reporter) {
1050 SkBitmap bm;
1051 make_bm(&bm, 10, 10, SK_ColorRED, true);
1052
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001053 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001054
robertphillips9f1c2412014-06-09 06:25:34 -07001055 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001056 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1057 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001058
robertphillips9f1c2412014-06-09 06:25:34 -07001059 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001060 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1061 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001062
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001063 {
robertphillips9f1c2412014-06-09 06:25:34 -07001064 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001065 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001066 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1067 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1068 }
1069 {
robertphillips9f1c2412014-06-09 06:25:34 -07001070 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001071 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001072 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1073 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1074 }
1075 {
robertphillips9f1c2412014-06-09 06:25:34 -07001076 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001077 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001078 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001079 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1080 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1081 }
1082 {
robertphillips9f1c2412014-06-09 06:25:34 -07001083 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001084 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001085 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001086 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1087 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1088 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001089}
1090
robertphillips@google.comd5500882014-04-02 23:51:13 +00001091static void test_gen_id(skiatest::Reporter* reporter) {
1092
Robert Phillipscfaeec42014-07-13 12:00:50 -04001093 SkPictureRecorder recorder;
1094 recorder.beginRecording(0, 0);
1095 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001096
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001097 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001098 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001099
robertphillips9f1c2412014-06-09 06:25:34 -07001100 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001101 canvas->drawARGB(255, 255, 255, 255);
1102 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1103 // picture should have a non-zero id after recording
1104 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001105
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001106 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001107 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001108}
1109
tomhudson158fcaa2014-11-19 10:41:14 -08001110static void test_bytes_used(skiatest::Reporter* reporter) {
1111 SkPictureRecorder recorder;
1112
1113 recorder.beginRecording(0, 0);
1114 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1115
1116 // Sanity check to make sure we aren't under-measuring.
1117 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >=
1118 sizeof(SkPicture) + sizeof(SkRecord));
1119
1120 // Protect against any unintentional bloat.
reed1bdfd3f2014-11-24 14:41:51 -08001121 size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
mtklein29b1afc2015-04-09 07:46:41 -07001122 REPORTER_ASSERT(reporter, approxUsed <= 416);
tomhudson158fcaa2014-11-19 10:41:14 -08001123
1124 // Sanity check of nested SkPictures.
1125 SkPictureRecorder r2;
1126 r2.beginRecording(0, 0);
1127 r2.getRecordingCanvas()->drawPicture(empty.get());
1128 SkAutoTUnref<SkPicture> nested(r2.endRecording());
1129
1130 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >
1131 SkPictureUtils::ApproximateBytesUsed(empty.get()));
1132}
1133
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001134DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001135#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001136 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001137 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001138#else
1139 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001140#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001141 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001142 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001143#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001144 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001145#endif
mtklein8e126562014-10-01 09:29:35 -07001146 test_has_text(reporter);
1147 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001148 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001149 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001150 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001151 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001152 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001153 test_savelayer_extraction(reporter);
tomhudson158fcaa2014-11-19 10:41:14 -08001154 test_bytes_used(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001155}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001156
1157static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1158 const SkPaint paint;
1159 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1160 const SkIRect irect = { 2, 2, 3, 3 };
1161
1162 // Don't care what these record, as long as they're legal.
1163 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1164 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001165 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1166 canvas->drawSprite(bitmap, 1, 1);
1167}
1168
1169static void test_draw_bitmaps(SkCanvas* canvas) {
1170 SkBitmap empty;
1171 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001172 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001173 draw_bitmaps(empty, canvas);
1174}
1175
1176DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001177 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001178 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001179 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001180}
1181
1182DEF_TEST(Canvas_EmptyBitmap, r) {
1183 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001184 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001185 SkCanvas canvas(dst);
1186
1187 test_draw_bitmaps(&canvas);
1188}
dneto3f22e8c2014-07-30 15:42:22 -07001189
1190DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1191 // This test is from crbug.com/344987.
1192 // The commands are:
1193 // saveLayer with paint that modifies alpha
1194 // drawBitmapRectToRect
1195 // drawBitmapRectToRect
1196 // restore
1197 // The bug was that this structure was modified so that:
1198 // - The saveLayer and restore were eliminated
1199 // - The alpha was only applied to the first drawBitmapRectToRect
1200
1201 // This test draws blue and red squares inside a 50% transparent
1202 // layer. Both colours should show up muted.
1203 // When the bug is present, the red square (the second bitmap)
1204 // shows upwith full opacity.
1205
1206 SkBitmap blueBM;
1207 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1208 SkBitmap redBM;
1209 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1210 SkPaint semiTransparent;
1211 semiTransparent.setAlpha(0x80);
1212
1213 SkPictureRecorder recorder;
1214 SkCanvas* canvas = recorder.beginRecording(100, 100);
1215 canvas->drawARGB(0, 0, 0, 0);
1216
1217 canvas->saveLayer(0, &semiTransparent);
1218 canvas->drawBitmap(blueBM, 25, 25);
1219 canvas->drawBitmap(redBM, 50, 50);
1220 canvas->restore();
1221
1222 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1223
1224 // Now replay the picture back on another canvas
1225 // and check a couple of its pixels.
1226 SkBitmap replayBM;
1227 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1228 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001229 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001230 replayCanvas.flush();
1231
1232 // With the bug present, at (55, 55) we would get a fully opaque red
1233 // intead of a dark red.
1234 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1235 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1236}
mtklein3e8232b2014-08-18 13:39:11 -07001237
1238struct CountingBBH : public SkBBoxHierarchy {
1239 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001240 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001241
schenney23d85932015-03-06 16:20:28 -08001242 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001243
mtklein36352bf2015-03-25 18:17:31 -07001244 void search(const SkRect& query, SkTDArray<unsigned>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001245 this->searchCalls++;
1246 }
1247
mtklein36352bf2015-03-25 18:17:31 -07001248 void insert(const SkRect[], int) override {}
1249 virtual size_t bytesUsed() const override { return 0; }
1250 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001251};
1252
1253class SpoonFedBBHFactory : public SkBBHFactory {
1254public:
1255 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001256 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001257 return SkRef(fBBH);
1258 }
1259private:
1260 SkBBoxHierarchy* fBBH;
1261};
1262
1263// When the canvas clip covers the full picture, we don't need to call the BBH.
1264DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001265 SkRect bound = SkRect::MakeWH(320, 240);
1266 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001267 SpoonFedBBHFactory factory(&bbh);
1268
1269 SkPictureRecorder recorder;
schenney23d85932015-03-06 16:20:28 -08001270 recorder.beginRecording(bound, &factory);
mtklein3e8232b2014-08-18 13:39:11 -07001271 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1272
1273 SkCanvas big(640, 480), small(300, 200);
1274
robertphillipsc5ba71d2014-09-04 08:42:50 -07001275 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001276 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1277
robertphillipsc5ba71d2014-09-04 08:42:50 -07001278 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001279 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1280}
mtkleind72094d2014-08-27 12:12:23 -07001281
1282DEF_TEST(Picture_BitmapLeak, r) {
1283 SkBitmap mut, immut;
1284 mut.allocN32Pixels(300, 200);
1285 immut.allocN32Pixels(300, 200);
1286 immut.setImmutable();
1287 SkASSERT(!mut.isImmutable());
1288 SkASSERT(immut.isImmutable());
1289
1290 // No one can hold a ref on our pixels yet.
1291 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1292 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1293
reed1bdfd3f2014-11-24 14:41:51 -08001294 SkAutoTUnref<const SkPicture> pic;
1295 {
1296 // we want the recorder to go out of scope before our subsequent checks, so we
1297 // place it inside local braces.
1298 SkPictureRecorder rec;
1299 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1300 canvas->drawBitmap(mut, 0, 0);
1301 canvas->drawBitmap(immut, 800, 600);
1302 pic.reset(rec.endRecording());
1303 }
mtkleind72094d2014-08-27 12:12:23 -07001304
1305 // The picture shares the immutable pixels but copies the mutable ones.
1306 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1307 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1308
1309 // When the picture goes away, it's just our bitmaps holding the refs.
1310 pic.reset(NULL);
1311 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1312 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1313}
mtkleinfeaadee2015-04-08 11:25:48 -07001314
1315// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1316DEF_TEST(Picture_getRecordingCanvas, r) {
1317 SkPictureRecorder rec;
1318 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1319 for (int i = 0; i < 3; i++) {
1320 rec.beginRecording(100, 100);
1321 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1322 rec.endRecording()->unref();
1323 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1324 }
1325}