blob: b63b35eea701fe98893c5eccdf178df950280e03 [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"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000031
32#if SK_SUPPORT_GPU
33#include "SkSurface.h"
34#include "GrContextFactory.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000035#endif
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000036#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000037
reed@google.com47b679b2014-05-14 18:58:16 +000038#include "SkLumaColorFilter.h"
39#include "SkColorFilterImageFilter.h"
40
reed@google.comfe7b1ed2012-11-29 21:00:39 +000041static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000042 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043 bm->eraseColor(color);
44 if (immutable) {
45 bm->setImmutable();
46 }
47}
48
tomhudson3a0f2792014-08-20 05:29:41 -070049/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070050static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070051 SkPictureRecorder recorder;
52
mtklein8e126562014-10-01 09:29:35 -070053 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070054 {
55 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
56 }
57 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
58 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
59
mtklein8e126562014-10-01 09:29:35 -070060 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070061 {
62 SkPaint paint;
63 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
64 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
65 SkBitmap bitmap;
66 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
67 bitmap.eraseColor(SK_ColorBLUE);
68 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
69 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
70 SkShader::kClamp_TileMode);
71 paint.setShader(shader)->unref();
72 REPORTER_ASSERT(reporter,
73 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
74
75 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
76 }
77 picture.reset(recorder.endRecording());
78 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
79}
80
81
scroggo@google.comd614c6a2012-09-14 17:26:37 +000082#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070083// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070084// in debug mode, so only run in debug mode.
85static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000086 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000087 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070088 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070089 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +000090 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -070091 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070092 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000093}
94
95// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
96static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000097 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -070098 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +000099 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000100 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000101 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000102}
103#endif
104
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000105static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000106 SkPaint paint;
107 SkRect rect = SkRect::MakeWH(50, 50);
108
109 SkScalar unit = rand.nextUScalar1();
110 if (unit <= 0.3) {
111// SkDebugf("save\n");
112 canvas->save();
113 } else if (unit <= 0.6) {
114// SkDebugf("restore\n");
115 canvas->restore();
116 } else if (unit <= 0.9) {
117// SkDebugf("clip\n");
118 canvas->clipRect(rect);
119 } else {
120// SkDebugf("draw\n");
121 canvas->drawPaint(paint);
122 }
123}
124
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000125#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700126
mtklein8e126562014-10-01 09:29:35 -0700127static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000128 SkPictureRecorder recorder;
129
mtklein8e126562014-10-01 09:29:35 -0700130 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000131 {
132 SkPath path;
133 path.moveTo(0, 0);
134 path.lineTo(50, 50);
135
136 SkScalar intervals[] = { 1.0f, 1.0f };
137 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
138
139 SkPaint paint;
140 paint.setStyle(SkPaint::kStroke_Style);
141 paint.setPathEffect(dash);
142
143 canvas->drawPath(path, paint);
144 }
145 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
146 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000147
148 const char *reason = NULL;
149 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700150 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000151
mtklein8e126562014-10-01 09:29:35 -0700152 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000153 {
154 SkPath path;
155
156 path.moveTo(0, 0);
157 path.lineTo(0, 50);
158 path.lineTo(25, 25);
159 path.lineTo(50, 50);
160 path.lineTo(50, 0);
161 path.close();
162 REPORTER_ASSERT(reporter, !path.isConvex());
163
164 SkPaint paint;
165 paint.setAntiAlias(true);
166 for (int i = 0; i < 50; ++i) {
167 canvas->drawPath(path, paint);
168 }
169 }
170 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800171 // A lot of small AA concave paths should be fine for GPU rendering
172 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
173
174 canvas = recorder.beginRecording(100, 100);
175 {
176 SkPath path;
177
178 path.moveTo(0, 0);
179 path.lineTo(0, 100);
180 path.lineTo(50, 50);
181 path.lineTo(100, 100);
182 path.lineTo(100, 0);
183 path.close();
184 REPORTER_ASSERT(reporter, !path.isConvex());
185
186 SkPaint paint;
187 paint.setAntiAlias(true);
188 for (int i = 0; i < 50; ++i) {
189 canvas->drawPath(path, paint);
190 }
191 }
192 picture.reset(recorder.endRecording());
193 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000194 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
195
mtklein8e126562014-10-01 09:29:35 -0700196 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000197 {
198 SkPath path;
199
200 path.moveTo(0, 0);
201 path.lineTo(0, 50);
202 path.lineTo(25, 25);
203 path.lineTo(50, 50);
204 path.lineTo(50, 0);
205 path.close();
206 REPORTER_ASSERT(reporter, !path.isConvex());
207
208 SkPaint paint;
209 paint.setAntiAlias(true);
210 paint.setStyle(SkPaint::kStroke_Style);
211 paint.setStrokeWidth(0);
212 for (int i = 0; i < 50; ++i) {
213 canvas->drawPath(path, paint);
214 }
215 }
216 picture.reset(recorder.endRecording());
217 // hairline stroked AA concave paths are fine for GPU rendering
218 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
tomhudson3a0f2792014-08-20 05:29:41 -0700219
mtklein8e126562014-10-01 09:29:35 -0700220 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700221 {
222 SkPaint paint;
223 SkScalar intervals [] = { 10, 20 };
224 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
225 paint.setPathEffect(pe)->unref();
226
227 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
228 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
229 }
230 picture.reset(recorder.endRecording());
231 // fast-path dashed effects are fine for GPU rendering ...
232 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
233
mtklein8e126562014-10-01 09:29:35 -0700234 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700235 {
236 SkPaint paint;
237 SkScalar intervals [] = { 10, 20 };
238 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
239 paint.setPathEffect(pe)->unref();
240
241 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
242 }
243 picture.reset(recorder.endRecording());
244 // ... but only when applied to drawPoint() calls
245 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
mtklein53fecfb2014-08-21 09:11:37 -0700246
247 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700248 canvas = recorder.beginRecording(100, 100);
249 {
250 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700251 }
mtklein8e126562014-10-01 09:29:35 -0700252 picture.reset(recorder.endRecording());
253 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000254}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000255
robertphillips82365912014-11-12 09:32:34 -0800256#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000257
robertphillips82365912014-11-12 09:32:34 -0800258static void test_savelayer_extraction(skiatest::Reporter* reporter) {
259 static const int kWidth = 100;
260 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000261
robertphillips82365912014-11-12 09:32:34 -0800262 // Create complex paint that the bounding box computation code can't
263 // optimize away
264 SkScalar blueToRedMatrix[20] = { 0 };
265 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
266 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
267 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000268
robertphillips82365912014-11-12 09:32:34 -0800269 SkPaint complexPaint;
270 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000271
robertphillips82365912014-11-12 09:32:34 -0800272 SkAutoTUnref<SkPicture> pict, child;
273 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000274
robertphillips82365912014-11-12 09:32:34 -0800275 {
276 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700277
robertphillips82365912014-11-12 09:32:34 -0800278 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
279 &bbhFactory,
280 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700281
robertphillips82365912014-11-12 09:32:34 -0800282 c->saveLayer(NULL, &complexPaint);
283 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700284
robertphillips82365912014-11-12 09:32:34 -0800285 child.reset(recorder.endRecording());
286 }
robertphillipsd6283302014-08-27 11:53:28 -0700287
robertphillips82365912014-11-12 09:32:34 -0800288 // create a picture with the structure:
289 // 1)
290 // SaveLayer
291 // Restore
292 // 2)
293 // SaveLayer
294 // Translate
295 // SaveLayer w/ bound
296 // Restore
297 // Restore
298 // 3)
299 // SaveLayer w/ copyable paint
300 // Restore
301 // 4)
302 // SaveLayer
303 // DrawPicture (which has a SaveLayer/Restore pair)
304 // Restore
305 // 5)
306 // SaveLayer
307 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
308 // Restore
309 {
310 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700311
robertphillips82365912014-11-12 09:32:34 -0800312 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
313 SkIntToScalar(kHeight),
314 &bbhFactory,
315 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000316 // 1)
robertphillips82365912014-11-12 09:32:34 -0800317 c->saveLayer(NULL, &complexPaint); // layer #0
318 c->restore();
319
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000320 // 2)
robertphillips82365912014-11-12 09:32:34 -0800321 c->saveLayer(NULL, NULL); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800322 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800323 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
324 c->saveLayer(&r, &complexPaint); // layer #2
325 c->restore();
326 c->restore();
327
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000328 // 3)
robertphillips82365912014-11-12 09:32:34 -0800329 {
330 c->saveLayer(NULL, &complexPaint); // layer #3
331 c->restore();
332 }
333
334 SkPaint layerPaint;
335 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000336 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000337 {
robertphillips82365912014-11-12 09:32:34 -0800338 c->saveLayer(NULL, &layerPaint); // layer #4
339 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000340 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800341 }
342 // 5
343 {
344 SkPaint picturePaint;
345 SkMatrix trans;
346 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700347
robertphillips82365912014-11-12 09:32:34 -0800348 c->saveLayer(NULL, &layerPaint); // layer #6
349 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700350 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000351 }
352
robertphillips82365912014-11-12 09:32:34 -0800353 pict.reset(recorder.endRecording());
354 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000355
robertphillips82365912014-11-12 09:32:34 -0800356 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800357 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
robertphillips82365912014-11-12 09:32:34 -0800358 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000359
robertphillips82365912014-11-12 09:32:34 -0800360 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
361 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000362
robertphillips82365912014-11-12 09:32:34 -0800363 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
364 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700365
robertphillips82365912014-11-12 09:32:34 -0800366 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
367 // The parent/child layers appear in reverse order
368 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
369 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700370
robertphillips82365912014-11-12 09:32:34 -0800371 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700372
robertphillips82365912014-11-12 09:32:34 -0800373 // The parent/child layers appear in reverse order
374 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
375 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000376
robertphillips82365912014-11-12 09:32:34 -0800377 // The parent/child layers appear in reverse order
378 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
379 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000380
robertphillips82365912014-11-12 09:32:34 -0800381 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
382 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
383 kHeight == info0.fBounds.height());
384 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
385 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
386 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
387 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
388 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000389
robertphillips82365912014-11-12 09:32:34 -0800390 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
391 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
392 kHeight/2.0 == info1.fBounds.height());
393 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
394 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800395 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800396 kHeight/2.0 == info1.fBounds.fTop);
397 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
398 REPORTER_ASSERT(reporter, !info1.fIsNested &&
399 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000400
robertphillips82365912014-11-12 09:32:34 -0800401 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
402 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
403 kHeight / 2 == info2.fBounds.height()); // bound reduces size
404 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
405 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
406 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
407 kHeight / 2 == info2.fBounds.fTop);
408 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
409 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000410
robertphillips82365912014-11-12 09:32:34 -0800411 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
412 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
413 kHeight == info3.fBounds.height());
414 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
415 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
416 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
417 REPORTER_ASSERT(reporter, info3.fPaint);
418 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700419
robertphillips82365912014-11-12 09:32:34 -0800420 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
421 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
422 kHeight == info4.fBounds.height());
423 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
424 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
425 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
426 REPORTER_ASSERT(reporter, info4.fPaint);
427 REPORTER_ASSERT(reporter, !info4.fIsNested &&
428 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700429
robertphillips82365912014-11-12 09:32:34 -0800430 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
431 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
432 kHeight == info5.fBounds.height());
433 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
434 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
435 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
436 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
437 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700438
robertphillips82365912014-11-12 09:32:34 -0800439 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
440 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
441 kHeight-10 == info6.fBounds.height());
442 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
443 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
444 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
445 REPORTER_ASSERT(reporter, info6.fPaint);
446 REPORTER_ASSERT(reporter, !info6.fIsNested &&
447 info6.fHasNestedLayers); // has a nested SL
448
449 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
450 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
451 kHeight == info7.fBounds.height());
452 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
453 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
454 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
455 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
456 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000457 }
458}
459
mtklein8e126562014-10-01 09:29:35 -0700460static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700461 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700462
mtklein8e126562014-10-01 09:29:35 -0700463 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700464 {
mtkleinc551d9f2014-08-20 08:09:46 -0700465 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700466 }
467 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
468 REPORTER_ASSERT(reporter, !picture->hasText());
469
mtkleinc551d9f2014-08-20 08:09:46 -0700470 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700471 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700472 {
mtkleinc551d9f2014-08-20 08:09:46 -0700473 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700474 }
475 picture.reset(recorder.endRecording());
476 REPORTER_ASSERT(reporter, picture->hasText());
477
mtklein8e126562014-10-01 09:29:35 -0700478 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700479 {
mtkleinc551d9f2014-08-20 08:09:46 -0700480 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700481 }
482 picture.reset(recorder.endRecording());
483 REPORTER_ASSERT(reporter, picture->hasText());
484
mtklein8e126562014-10-01 09:29:35 -0700485 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700486 {
mtkleinc551d9f2014-08-20 08:09:46 -0700487 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700488 }
489 picture.reset(recorder.endRecording());
490 REPORTER_ASSERT(reporter, picture->hasText());
491
mtklein8e126562014-10-01 09:29:35 -0700492 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700493 {
494 SkPath path;
495 path.moveTo(0, 0);
496 path.lineTo(50, 50);
497
mtkleinc551d9f2014-08-20 08:09:46 -0700498 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700499 }
500 picture.reset(recorder.endRecording());
501 REPORTER_ASSERT(reporter, picture->hasText());
502
mtklein8e126562014-10-01 09:29:35 -0700503 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700504 {
505 SkPath path;
506 path.moveTo(0, 0);
507 path.lineTo(50, 50);
508
mtkleinc551d9f2014-08-20 08:09:46 -0700509 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700510 }
511 picture.reset(recorder.endRecording());
512 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700513
514 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700515 canvas = recorder.beginRecording(100,100);
516 {
517 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700518 }
mtklein8e126562014-10-01 09:29:35 -0700519 picture.reset(recorder.endRecording());
520 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700521}
522
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000523static void set_canvas_to_save_count_4(SkCanvas* canvas) {
524 canvas->restoreToCount(1);
525 canvas->save();
526 canvas->save();
527 canvas->save();
528}
529
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000530/**
531 * A canvas that records the number of saves, saveLayers and restores.
532 */
533class SaveCountingCanvas : public SkCanvas {
534public:
535 SaveCountingCanvas(int width, int height)
536 : INHERITED(width, height)
537 , fSaveCount(0)
538 , fSaveLayerCount(0)
539 , fRestoreCount(0){
540 }
541
542 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
543 SaveFlags flags) SK_OVERRIDE {
544 ++fSaveLayerCount;
545 return this->INHERITED::willSaveLayer(bounds, paint, flags);
546 }
547
mtklein72c9faa2015-01-09 10:06:39 -0800548 void willSave() SK_OVERRIDE {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000549 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400550 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000551 }
552
mtklein72c9faa2015-01-09 10:06:39 -0800553 void willRestore() SK_OVERRIDE {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000554 ++fRestoreCount;
555 this->INHERITED::willRestore();
556 }
557
558 unsigned int getSaveCount() const { return fSaveCount; }
559 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
560 unsigned int getRestoreCount() const { return fRestoreCount; }
561
562private:
563 unsigned int fSaveCount;
564 unsigned int fSaveLayerCount;
565 unsigned int fRestoreCount;
566
567 typedef SkCanvas INHERITED;
568};
569
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000570void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000571 unsigned int numSaves, unsigned int numSaveLayers,
572 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700573 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700574 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000575
robertphillipsc5ba71d2014-09-04 08:42:50 -0700576 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000577
mtklein87c41382014-09-08 07:31:18 -0700578 // Optimizations may have removed these,
579 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
580 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
581 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
582 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000583}
584
585// This class exists so SkPicture can friend it and give it access to
586// the 'partialReplay' method.
587class SkPictureRecorderReplayTester {
588public:
589 static SkPicture* Copy(SkPictureRecorder* recorder) {
590 SkPictureRecorder recorder2;
591
robertphillips9f1c2412014-06-09 06:25:34 -0700592 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000593
594 recorder->partialReplay(canvas);
595
596 return recorder2.endRecording();
597 }
598};
599
robertphillips9058d602014-06-10 11:45:46 -0700600static void create_imbalance(SkCanvas* canvas) {
601 SkRect clipRect = SkRect::MakeWH(2, 2);
602 SkRect drawRect = SkRect::MakeWH(10, 10);
603 canvas->save();
604 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
605 canvas->translate(1.0f, 1.0f);
606 SkPaint p;
607 p.setColor(SK_ColorGREEN);
608 canvas->drawRect(drawRect, p);
609 // no restore
610}
611
612// This tests that replaying a potentially unbalanced picture into a canvas
613// doesn't affect the canvas' save count or matrix/clip state.
614static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
615 SkBitmap bm;
616 bm.allocN32Pixels(4, 3);
617 SkCanvas canvas(bm);
618
619 int beforeSaveCount = canvas.getSaveCount();
620
621 SkMatrix beforeMatrix = canvas.getTotalMatrix();
622
623 SkRect beforeClip;
624
625 canvas.getClipBounds(&beforeClip);
626
627 canvas.drawPicture(picture);
628
629 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
630 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
631
632 SkRect afterClip;
633
634 canvas.getClipBounds(&afterClip);
635
636 REPORTER_ASSERT(reporter, afterClip == beforeClip);
637}
638
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000639// Test out SkPictureRecorder::partialReplay
640DEF_TEST(PictureRecorder_replay, reporter) {
641 // check save/saveLayer state
642 {
643 SkPictureRecorder recorder;
644
robertphillips9f1c2412014-06-09 06:25:34 -0700645 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000646
647 canvas->saveLayer(NULL, NULL);
648
649 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
650
651 // The extra save and restore comes from the Copy process.
652 check_save_state(reporter, copy, 2, 1, 3);
653
654 canvas->saveLayer(NULL, NULL);
655
656 SkAutoTUnref<SkPicture> final(recorder.endRecording());
657
658 check_save_state(reporter, final, 1, 2, 3);
659
660 // The copy shouldn't pick up any operations added after it was made
661 check_save_state(reporter, copy, 2, 1, 3);
662 }
663
664 // (partially) check leakage of draw ops
665 {
666 SkPictureRecorder recorder;
667
robertphillips9f1c2412014-06-09 06:25:34 -0700668 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000669
670 SkRect r = SkRect::MakeWH(5, 5);
671 SkPaint p;
672
673 canvas->drawRect(r, p);
674
675 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
676
677 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
678
679 SkBitmap bm;
680 make_bm(&bm, 10, 10, SK_ColorRED, true);
681
682 r.offset(5.0f, 5.0f);
683 canvas->drawBitmapRectToRect(bm, NULL, r);
684
685 SkAutoTUnref<SkPicture> final(recorder.endRecording());
686 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
687
688 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
689
690 // The snapshot shouldn't pick up any operations added after it was made
691 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
692 }
robertphillips9058d602014-06-10 11:45:46 -0700693
694 // Recreate the Android partialReplay test case
695 {
696 SkPictureRecorder recorder;
697
698 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
699 create_imbalance(canvas);
700
701 int expectedSaveCount = canvas->getSaveCount();
702
703 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
704 check_balance(reporter, copy);
705
706 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
707
708 // End the recording of source to test the picture finalization
709 // process isn't complicated by the partialReplay step
710 SkAutoTUnref<SkPicture> final(recorder.endRecording());
711 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000712}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000713
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000714static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
715 SkCanvas testCanvas(100, 100);
716 set_canvas_to_save_count_4(&testCanvas);
717
718 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
719
720 SkPaint paint;
721 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
722
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000723 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000724
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000725 {
726 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700727 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000728 canvas->save();
729 canvas->translate(10, 10);
730 canvas->drawRect(rect, paint);
731 canvas->save();
732 canvas->translate(10, 10);
733 canvas->drawRect(rect, paint);
734 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
735
robertphillips9b14f262014-06-04 05:40:44 -0700736 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000737 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
738 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000739
740 set_canvas_to_save_count_4(&testCanvas);
741
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000742 {
743 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700744 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000745 canvas->save();
746 canvas->translate(10, 10);
747 canvas->drawRect(rect, paint);
748 canvas->save();
749 canvas->translate(10, 10);
750 canvas->drawRect(rect, paint);
751 canvas->restore();
752 canvas->restore();
753 canvas->restore();
754 canvas->restore();
755 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000756
robertphillips9b14f262014-06-04 05:40:44 -0700757 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000758 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
759 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000760
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000761 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000762
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000763 {
robertphillips9f1c2412014-06-09 06:25:34 -0700764 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000765 canvas->translate(10, 10);
766 canvas->drawRect(rect, paint);
767 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
768
robertphillips9b14f262014-06-04 05:40:44 -0700769 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000770 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
771 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
772 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000773}
774
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000775static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000776 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000777
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000778 SkPictureRecorder recorder;
779
reed@google.com21b519d2012-10-02 17:42:15 +0000780 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000781 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000782
robertphillips9f1c2412014-06-09 06:25:34 -0700783 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000784
785 for (int i = 0; i < 1000; ++i) {
786 rand_op(canvas, rand);
787 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000788 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000789
790 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000791 }
792
793 {
robertphillips9f1c2412014-06-09 06:25:34 -0700794 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000795 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000796
reed@google.com21b519d2012-10-02 17:42:15 +0000797 for (int i = 0; i < 100; ++i) {
798 canvas->save();
799 }
800 while (canvas->getSaveCount() > 1) {
801 canvas->clipRect(rect);
802 canvas->restore();
803 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000804 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000805 }
806}
807
scroggo@google.com4b90b112012-12-04 15:08:56 +0000808#ifndef SK_DEBUG
809// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
810// should never do this.
811static void test_bad_bitmap() {
812 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
813 // fail.
814 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000815 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000816 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700817 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000818 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000819 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000820
821 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700822 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000823}
824#endif
825
scroggo895c43b2014-12-11 10:53:58 -0800826// Encodes to PNG, unless there is already encoded data, in which case that gets
827// used.
828// FIXME: Share with PictureRenderer.cpp?
829class PngPixelSerializer : public SkPixelSerializer {
830public:
reed53b9e2e2014-12-19 12:26:07 -0800831 bool onUseEncodedData(const void*, size_t) SK_OVERRIDE { return true; }
832 SkData* onEncodePixels(const SkImageInfo& info, const void* pixels,
833 size_t rowBytes) SK_OVERRIDE {
834 return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100);
scroggo895c43b2014-12-11 10:53:58 -0800835 }
836};
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000837
838static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000839 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700840 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700841 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000842 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000843 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
844
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000845 SkDynamicMemoryWStream wStream;
scroggo895c43b2014-12-11 10:53:58 -0800846 PngPixelSerializer serializer;
847 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000848 return wStream.copyToData();
849}
850
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000851struct ErrorContext {
852 int fErrors;
853 skiatest::Reporter* fReporter;
854};
855
856static void assert_one_parse_error_cb(SkError error, void* context) {
857 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
858 errorContext->fErrors++;
859 // This test only expects one error, and that is a kParseError. If there are others,
860 // there is some unknown problem.
861 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
862 "This threw more errors than expected.");
863 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
864 SkGetLastErrorString());
865}
866
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000867static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
868 // Create a bitmap that will be encoded.
869 SkBitmap original;
870 make_bm(&original, 100, 100, SK_ColorBLUE, true);
871 SkDynamicMemoryWStream wStream;
872 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
873 return;
874 }
875 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000876
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000877 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800878 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000879 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000880
881 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
882 // Flattening original will follow the old path of performing an encode, while flattening bm
883 // will use the already encoded data.
884 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
885 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
886 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000887 // Now test that a parse error was generated when trying to create a new SkPicture without
888 // providing a function to decode the bitmap.
889 ErrorContext context;
890 context.fErrors = 0;
891 context.fReporter = reporter;
892 SkSetErrorCallback(assert_one_parse_error_cb, &context);
893 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000894 SkClearLastError();
mtklein08d1fcc2014-11-20 09:18:31 -0800895 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000896 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000897 SkClearLastError();
898 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000899}
900
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000901static void test_clip_bound_opt(skiatest::Reporter* reporter) {
902 // Test for crbug.com/229011
903 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
904 SkIntToScalar(2), SkIntToScalar(2));
905 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
906 SkIntToScalar(1), SkIntToScalar(1));
907 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
908 SkIntToScalar(1), SkIntToScalar(1));
909
910 SkPath invPath;
911 invPath.addOval(rect1);
912 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
913 SkPath path;
914 path.addOval(rect2);
915 SkPath path2;
916 path2.addOval(rect3);
917 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000918 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700919
920 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000921 {
robertphillips9f1c2412014-06-09 06:25:34 -0700922 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000923 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
924 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
925 REPORTER_ASSERT(reporter, true == nonEmpty);
926 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
927 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
928 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
929 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
930 }
931 {
robertphillips9f1c2412014-06-09 06:25:34 -0700932 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000933 canvas->clipPath(path, SkRegion::kIntersect_Op);
934 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
935 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
936 REPORTER_ASSERT(reporter, true == nonEmpty);
937 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
938 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
939 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
940 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
941 }
942 {
robertphillips9f1c2412014-06-09 06:25:34 -0700943 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000944 canvas->clipPath(path, SkRegion::kIntersect_Op);
945 canvas->clipPath(invPath, SkRegion::kUnion_Op);
946 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
947 REPORTER_ASSERT(reporter, true == nonEmpty);
948 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
949 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
950 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
951 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
952 }
953 {
robertphillips9f1c2412014-06-09 06:25:34 -0700954 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000955 canvas->clipPath(path, SkRegion::kDifference_Op);
956 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
957 REPORTER_ASSERT(reporter, true == nonEmpty);
958 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
959 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
960 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
961 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
962 }
963 {
robertphillips9f1c2412014-06-09 06:25:34 -0700964 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000965 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
966 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
967 // True clip is actually empty in this case, but the best
968 // determination we can make using only bounds as input is that the
969 // clip is included in the bounds of 'path'.
970 REPORTER_ASSERT(reporter, true == nonEmpty);
971 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
972 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
973 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
974 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
975 }
976 {
robertphillips9f1c2412014-06-09 06:25:34 -0700977 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000978 canvas->clipPath(path, SkRegion::kIntersect_Op);
979 canvas->clipPath(path2, SkRegion::kXOR_Op);
980 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
981 REPORTER_ASSERT(reporter, true == nonEmpty);
982 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
983 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
984 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
985 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
986 }
987}
988
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000989/**
990 * A canvas that records the number of clip commands.
991 */
992class ClipCountingCanvas : public SkCanvas {
993public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000994 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000995 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000996 , fClipCount(0){
997 }
998
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000999 virtual void onClipRect(const SkRect& r,
1000 SkRegion::Op op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001001 ClipEdgeStyle edgeStyle) SK_OVERRIDE {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001002 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001003 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001004 }
1005
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001006 virtual void onClipRRect(const SkRRect& rrect,
1007 SkRegion::Op op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001008 ClipEdgeStyle edgeStyle)SK_OVERRIDE {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001009 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001010 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001011 }
1012
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001013 virtual void onClipPath(const SkPath& path,
1014 SkRegion::Op op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001015 ClipEdgeStyle edgeStyle) SK_OVERRIDE {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001016 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001017 this->INHERITED::onClipPath(path, op, edgeStyle);
1018 }
1019
mtklein72c9faa2015-01-09 10:06:39 -08001020 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001021 fClipCount += 1;
1022 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001023 }
1024
1025 unsigned getClipCount() const { return fClipCount; }
1026
1027private:
1028 unsigned fClipCount;
1029
1030 typedef SkCanvas INHERITED;
1031};
1032
1033static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001034 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001035 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001036
1037 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1038 // The following expanding clip should not be skipped.
1039 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1040 // Draw something so the optimizer doesn't just fold the world.
1041 SkPaint p;
1042 p.setColor(SK_ColorBLUE);
1043 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001044 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001045
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001046 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001047 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001048
1049 // Both clips should be present on playback.
1050 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1051}
1052
tomhudson@google.com381010e2013-10-24 11:12:47 +00001053static void test_hierarchical(skiatest::Reporter* reporter) {
1054 SkBitmap bm;
1055 make_bm(&bm, 10, 10, SK_ColorRED, true);
1056
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001057 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001058
robertphillips9f1c2412014-06-09 06:25:34 -07001059 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001060 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1061 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001062
robertphillips9f1c2412014-06-09 06:25:34 -07001063 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001064 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1065 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001066
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001067 {
robertphillips9f1c2412014-06-09 06:25:34 -07001068 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001069 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001070 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1071 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1072 }
1073 {
robertphillips9f1c2412014-06-09 06:25:34 -07001074 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001075 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001076 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1077 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1078 }
1079 {
robertphillips9f1c2412014-06-09 06:25:34 -07001080 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001081 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001082 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001083 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1084 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1085 }
1086 {
robertphillips9f1c2412014-06-09 06:25:34 -07001087 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001088 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001089 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001090 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1091 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1092 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001093}
1094
robertphillips@google.comd5500882014-04-02 23:51:13 +00001095static void test_gen_id(skiatest::Reporter* reporter) {
1096
Robert Phillipscfaeec42014-07-13 12:00:50 -04001097 SkPictureRecorder recorder;
1098 recorder.beginRecording(0, 0);
1099 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001100
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001101 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001102 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001103
robertphillips9f1c2412014-06-09 06:25:34 -07001104 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001105 canvas->drawARGB(255, 255, 255, 255);
1106 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1107 // picture should have a non-zero id after recording
1108 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001109
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001110 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001111 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001112}
1113
tomhudson158fcaa2014-11-19 10:41:14 -08001114static void test_bytes_used(skiatest::Reporter* reporter) {
1115 SkPictureRecorder recorder;
1116
1117 recorder.beginRecording(0, 0);
1118 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1119
1120 // Sanity check to make sure we aren't under-measuring.
1121 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >=
1122 sizeof(SkPicture) + sizeof(SkRecord));
1123
1124 // Protect against any unintentional bloat.
reed1bdfd3f2014-11-24 14:41:51 -08001125 size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
1126 REPORTER_ASSERT(reporter, approxUsed <= 136);
tomhudson158fcaa2014-11-19 10:41:14 -08001127
1128 // Sanity check of nested SkPictures.
1129 SkPictureRecorder r2;
1130 r2.beginRecording(0, 0);
1131 r2.getRecordingCanvas()->drawPicture(empty.get());
1132 SkAutoTUnref<SkPicture> nested(r2.endRecording());
1133
1134 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >
1135 SkPictureUtils::ApproximateBytesUsed(empty.get()));
1136}
1137
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001138DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001139#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001140 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001141 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001142#else
1143 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001144#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001145 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001146 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001147#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001148 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001149#endif
mtklein8e126562014-10-01 09:29:35 -07001150 test_has_text(reporter);
1151 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001152 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001153 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001154 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001155 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001156 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001157 test_savelayer_extraction(reporter);
tomhudson158fcaa2014-11-19 10:41:14 -08001158 test_bytes_used(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001159}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001160
1161static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1162 const SkPaint paint;
1163 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1164 const SkIRect irect = { 2, 2, 3, 3 };
1165
1166 // Don't care what these record, as long as they're legal.
1167 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1168 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001169 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1170 canvas->drawSprite(bitmap, 1, 1);
1171}
1172
1173static void test_draw_bitmaps(SkCanvas* canvas) {
1174 SkBitmap empty;
1175 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001176 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001177 draw_bitmaps(empty, canvas);
1178}
1179
1180DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001181 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001182 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001183 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001184}
1185
1186DEF_TEST(Canvas_EmptyBitmap, r) {
1187 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001188 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001189 SkCanvas canvas(dst);
1190
1191 test_draw_bitmaps(&canvas);
1192}
dneto3f22e8c2014-07-30 15:42:22 -07001193
1194DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1195 // This test is from crbug.com/344987.
1196 // The commands are:
1197 // saveLayer with paint that modifies alpha
1198 // drawBitmapRectToRect
1199 // drawBitmapRectToRect
1200 // restore
1201 // The bug was that this structure was modified so that:
1202 // - The saveLayer and restore were eliminated
1203 // - The alpha was only applied to the first drawBitmapRectToRect
1204
1205 // This test draws blue and red squares inside a 50% transparent
1206 // layer. Both colours should show up muted.
1207 // When the bug is present, the red square (the second bitmap)
1208 // shows upwith full opacity.
1209
1210 SkBitmap blueBM;
1211 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1212 SkBitmap redBM;
1213 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1214 SkPaint semiTransparent;
1215 semiTransparent.setAlpha(0x80);
1216
1217 SkPictureRecorder recorder;
1218 SkCanvas* canvas = recorder.beginRecording(100, 100);
1219 canvas->drawARGB(0, 0, 0, 0);
1220
1221 canvas->saveLayer(0, &semiTransparent);
1222 canvas->drawBitmap(blueBM, 25, 25);
1223 canvas->drawBitmap(redBM, 50, 50);
1224 canvas->restore();
1225
1226 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1227
1228 // Now replay the picture back on another canvas
1229 // and check a couple of its pixels.
1230 SkBitmap replayBM;
1231 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1232 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001233 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001234 replayCanvas.flush();
1235
1236 // With the bug present, at (55, 55) we would get a fully opaque red
1237 // intead of a dark red.
1238 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1239 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1240}
mtklein3e8232b2014-08-18 13:39:11 -07001241
1242struct CountingBBH : public SkBBoxHierarchy {
1243 mutable int searchCalls;
1244
1245 CountingBBH() : searchCalls(0) {}
1246
mtklein72c9faa2015-01-09 10:06:39 -08001247 void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {
mtklein3e8232b2014-08-18 13:39:11 -07001248 this->searchCalls++;
1249 }
1250
mtklein72c9faa2015-01-09 10:06:39 -08001251 void insert(SkAutoTMalloc<SkRect>*, int) SK_OVERRIDE {}
tomhudson158fcaa2014-11-19 10:41:14 -08001252 virtual size_t bytesUsed() const { return 0; }
mtklein3e8232b2014-08-18 13:39:11 -07001253};
1254
1255class SpoonFedBBHFactory : public SkBBHFactory {
1256public:
1257 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
reed78e27682014-11-19 08:04:34 -08001258 SkBBoxHierarchy* operator()(const SkRect&) const SK_OVERRIDE {
mtklein3e8232b2014-08-18 13:39:11 -07001259 return SkRef(fBBH);
1260 }
1261private:
1262 SkBBoxHierarchy* fBBH;
1263};
1264
1265// When the canvas clip covers the full picture, we don't need to call the BBH.
1266DEF_TEST(Picture_SkipBBH, r) {
1267 CountingBBH bbh;
1268 SpoonFedBBHFactory factory(&bbh);
1269
1270 SkPictureRecorder recorder;
1271 recorder.beginRecording(320, 240, &factory);
1272 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1273
1274 SkCanvas big(640, 480), small(300, 200);
1275
robertphillipsc5ba71d2014-09-04 08:42:50 -07001276 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001277 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1278
robertphillipsc5ba71d2014-09-04 08:42:50 -07001279 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001280 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1281}
mtkleind72094d2014-08-27 12:12:23 -07001282
1283DEF_TEST(Picture_BitmapLeak, r) {
1284 SkBitmap mut, immut;
1285 mut.allocN32Pixels(300, 200);
1286 immut.allocN32Pixels(300, 200);
1287 immut.setImmutable();
1288 SkASSERT(!mut.isImmutable());
1289 SkASSERT(immut.isImmutable());
1290
1291 // No one can hold a ref on our pixels yet.
1292 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1293 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1294
reed1bdfd3f2014-11-24 14:41:51 -08001295 SkAutoTUnref<const SkPicture> pic;
1296 {
1297 // we want the recorder to go out of scope before our subsequent checks, so we
1298 // place it inside local braces.
1299 SkPictureRecorder rec;
1300 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1301 canvas->drawBitmap(mut, 0, 0);
1302 canvas->drawBitmap(immut, 800, 600);
1303 pic.reset(rec.endRecording());
1304 }
mtkleind72094d2014-08-27 12:12:23 -07001305
1306 // The picture shares the immutable pixels but copies the mutable ones.
1307 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1308 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1309
1310 // When the picture goes away, it's just our bitmaps holding the refs.
1311 pic.reset(NULL);
1312 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1313 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1314}