blob: 33e058cc2e483fd5f185b501308f984172174990 [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
robertphillips98b03152015-01-26 11:29:36 -0800143 for (int i = 0; i < 50; ++i) {
144 canvas->drawPath(path, paint);
145 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000146 }
147 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
148 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000149
150 const char *reason = NULL;
151 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700152 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000153
mtklein8e126562014-10-01 09:29:35 -0700154 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000155 {
156 SkPath path;
157
158 path.moveTo(0, 0);
159 path.lineTo(0, 50);
160 path.lineTo(25, 25);
161 path.lineTo(50, 50);
162 path.lineTo(50, 0);
163 path.close();
164 REPORTER_ASSERT(reporter, !path.isConvex());
165
166 SkPaint paint;
167 paint.setAntiAlias(true);
168 for (int i = 0; i < 50; ++i) {
169 canvas->drawPath(path, paint);
170 }
171 }
172 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800173 // A lot of small AA concave paths should be fine for GPU rendering
174 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
175
176 canvas = recorder.beginRecording(100, 100);
177 {
178 SkPath path;
179
180 path.moveTo(0, 0);
181 path.lineTo(0, 100);
182 path.lineTo(50, 50);
183 path.lineTo(100, 100);
184 path.lineTo(100, 0);
185 path.close();
186 REPORTER_ASSERT(reporter, !path.isConvex());
187
188 SkPaint paint;
189 paint.setAntiAlias(true);
190 for (int i = 0; i < 50; ++i) {
191 canvas->drawPath(path, paint);
192 }
193 }
194 picture.reset(recorder.endRecording());
195 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000196 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
197
mtklein8e126562014-10-01 09:29:35 -0700198 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000199 {
200 SkPath path;
201
202 path.moveTo(0, 0);
203 path.lineTo(0, 50);
204 path.lineTo(25, 25);
205 path.lineTo(50, 50);
206 path.lineTo(50, 0);
207 path.close();
208 REPORTER_ASSERT(reporter, !path.isConvex());
209
210 SkPaint paint;
211 paint.setAntiAlias(true);
212 paint.setStyle(SkPaint::kStroke_Style);
213 paint.setStrokeWidth(0);
214 for (int i = 0; i < 50; ++i) {
215 canvas->drawPath(path, paint);
216 }
217 }
218 picture.reset(recorder.endRecording());
219 // hairline stroked AA concave paths are fine for GPU rendering
220 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
tomhudson3a0f2792014-08-20 05:29:41 -0700221
mtklein8e126562014-10-01 09:29:35 -0700222 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700223 {
224 SkPaint paint;
225 SkScalar intervals [] = { 10, 20 };
226 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
227 paint.setPathEffect(pe)->unref();
228
229 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800230
231 for (int i = 0; i < 50; ++i) {
232 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
233 }
tomhudson3a0f2792014-08-20 05:29:41 -0700234 }
235 picture.reset(recorder.endRecording());
236 // fast-path dashed effects are fine for GPU rendering ...
237 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
238
mtklein8e126562014-10-01 09:29:35 -0700239 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700240 {
241 SkPaint paint;
242 SkScalar intervals [] = { 10, 20 };
243 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
244 paint.setPathEffect(pe)->unref();
245
robertphillips98b03152015-01-26 11:29:36 -0800246 for (int i = 0; i < 50; ++i) {
247 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
248 }
tomhudson3a0f2792014-08-20 05:29:41 -0700249 }
250 picture.reset(recorder.endRecording());
251 // ... but only when applied to drawPoint() calls
252 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
mtklein53fecfb2014-08-21 09:11:37 -0700253
254 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700255 canvas = recorder.beginRecording(100, 100);
256 {
257 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700258 }
mtklein8e126562014-10-01 09:29:35 -0700259 picture.reset(recorder.endRecording());
260 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000261}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000262
robertphillips82365912014-11-12 09:32:34 -0800263#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000264
robertphillips82365912014-11-12 09:32:34 -0800265static void test_savelayer_extraction(skiatest::Reporter* reporter) {
266 static const int kWidth = 100;
267 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000268
robertphillips82365912014-11-12 09:32:34 -0800269 // Create complex paint that the bounding box computation code can't
270 // optimize away
271 SkScalar blueToRedMatrix[20] = { 0 };
272 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
273 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
274 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000275
robertphillips82365912014-11-12 09:32:34 -0800276 SkPaint complexPaint;
277 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000278
robertphillips82365912014-11-12 09:32:34 -0800279 SkAutoTUnref<SkPicture> pict, child;
280 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000281
robertphillips82365912014-11-12 09:32:34 -0800282 {
283 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700284
robertphillips82365912014-11-12 09:32:34 -0800285 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
286 &bbhFactory,
287 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700288
robertphillips82365912014-11-12 09:32:34 -0800289 c->saveLayer(NULL, &complexPaint);
290 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700291
robertphillips82365912014-11-12 09:32:34 -0800292 child.reset(recorder.endRecording());
293 }
robertphillipsd6283302014-08-27 11:53:28 -0700294
robertphillips82365912014-11-12 09:32:34 -0800295 // create a picture with the structure:
296 // 1)
297 // SaveLayer
298 // Restore
299 // 2)
300 // SaveLayer
301 // Translate
302 // SaveLayer w/ bound
303 // Restore
304 // Restore
305 // 3)
306 // SaveLayer w/ copyable paint
307 // Restore
308 // 4)
309 // SaveLayer
310 // DrawPicture (which has a SaveLayer/Restore pair)
311 // Restore
312 // 5)
313 // SaveLayer
314 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
315 // Restore
316 {
317 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700318
robertphillips82365912014-11-12 09:32:34 -0800319 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
320 SkIntToScalar(kHeight),
321 &bbhFactory,
322 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000323 // 1)
robertphillips82365912014-11-12 09:32:34 -0800324 c->saveLayer(NULL, &complexPaint); // layer #0
325 c->restore();
326
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000327 // 2)
robertphillips82365912014-11-12 09:32:34 -0800328 c->saveLayer(NULL, NULL); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800329 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800330 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
331 c->saveLayer(&r, &complexPaint); // layer #2
332 c->restore();
333 c->restore();
334
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000335 // 3)
robertphillips82365912014-11-12 09:32:34 -0800336 {
337 c->saveLayer(NULL, &complexPaint); // layer #3
338 c->restore();
339 }
340
341 SkPaint layerPaint;
342 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000343 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000344 {
robertphillips82365912014-11-12 09:32:34 -0800345 c->saveLayer(NULL, &layerPaint); // layer #4
346 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000347 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800348 }
349 // 5
350 {
351 SkPaint picturePaint;
352 SkMatrix trans;
353 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700354
robertphillips82365912014-11-12 09:32:34 -0800355 c->saveLayer(NULL, &layerPaint); // layer #6
356 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700357 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000358 }
359
robertphillips82365912014-11-12 09:32:34 -0800360 pict.reset(recorder.endRecording());
361 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000362
robertphillips82365912014-11-12 09:32:34 -0800363 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800364 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
robertphillips82365912014-11-12 09:32:34 -0800365 SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000366
robertphillips82365912014-11-12 09:32:34 -0800367 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
368 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000369
robertphillips82365912014-11-12 09:32:34 -0800370 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
371 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700372
robertphillips82365912014-11-12 09:32:34 -0800373 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
374 // The parent/child layers appear in reverse order
375 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
376 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700377
robertphillips82365912014-11-12 09:32:34 -0800378 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700379
robertphillips82365912014-11-12 09:32:34 -0800380 // The parent/child layers appear in reverse order
381 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
382 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000383
robertphillips82365912014-11-12 09:32:34 -0800384 // The parent/child layers appear in reverse order
385 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
386 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000387
robertphillips82365912014-11-12 09:32:34 -0800388 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
389 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
390 kHeight == info0.fBounds.height());
391 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
392 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
393 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
394 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
395 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000396
robertphillips82365912014-11-12 09:32:34 -0800397 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
398 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
399 kHeight/2.0 == info1.fBounds.height());
400 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
401 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800402 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800403 kHeight/2.0 == info1.fBounds.fTop);
404 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
405 REPORTER_ASSERT(reporter, !info1.fIsNested &&
406 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000407
robertphillips82365912014-11-12 09:32:34 -0800408 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
409 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
410 kHeight / 2 == info2.fBounds.height()); // bound reduces size
411 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
412 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
413 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
414 kHeight / 2 == info2.fBounds.fTop);
415 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
416 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000417
robertphillips82365912014-11-12 09:32:34 -0800418 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
419 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
420 kHeight == info3.fBounds.height());
421 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
422 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
423 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
424 REPORTER_ASSERT(reporter, info3.fPaint);
425 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700426
robertphillips82365912014-11-12 09:32:34 -0800427 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
428 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
429 kHeight == info4.fBounds.height());
430 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
431 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
432 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
433 REPORTER_ASSERT(reporter, info4.fPaint);
434 REPORTER_ASSERT(reporter, !info4.fIsNested &&
435 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700436
robertphillips82365912014-11-12 09:32:34 -0800437 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
438 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
439 kHeight == info5.fBounds.height());
440 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
441 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
442 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
443 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
444 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700445
robertphillips82365912014-11-12 09:32:34 -0800446 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
447 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
448 kHeight-10 == info6.fBounds.height());
449 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
450 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
451 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
452 REPORTER_ASSERT(reporter, info6.fPaint);
453 REPORTER_ASSERT(reporter, !info6.fIsNested &&
454 info6.fHasNestedLayers); // has a nested SL
455
456 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
457 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
458 kHeight == info7.fBounds.height());
459 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
460 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
461 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
462 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
463 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000464 }
465}
466
mtklein8e126562014-10-01 09:29:35 -0700467static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700468 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700469
mtklein8e126562014-10-01 09:29:35 -0700470 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700471 {
mtkleinc551d9f2014-08-20 08:09:46 -0700472 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700473 }
474 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
475 REPORTER_ASSERT(reporter, !picture->hasText());
476
mtkleinc551d9f2014-08-20 08:09:46 -0700477 SkPoint point = SkPoint::Make(10, 10);
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->drawText("Q", 1, point.fX, point.fY, 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->drawPosText("Q", 1, &point, 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 {
mtkleinc551d9f2014-08-20 08:09:46 -0700494 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700495 }
496 picture.reset(recorder.endRecording());
497 REPORTER_ASSERT(reporter, picture->hasText());
498
mtklein8e126562014-10-01 09:29:35 -0700499 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700500 {
501 SkPath path;
502 path.moveTo(0, 0);
503 path.lineTo(50, 50);
504
mtkleinc551d9f2014-08-20 08:09:46 -0700505 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700506 }
507 picture.reset(recorder.endRecording());
508 REPORTER_ASSERT(reporter, picture->hasText());
509
mtklein8e126562014-10-01 09:29:35 -0700510 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700511 {
512 SkPath path;
513 path.moveTo(0, 0);
514 path.lineTo(50, 50);
515
mtkleinc551d9f2014-08-20 08:09:46 -0700516 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700517 }
518 picture.reset(recorder.endRecording());
519 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700520
521 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700522 canvas = recorder.beginRecording(100,100);
523 {
524 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700525 }
mtklein8e126562014-10-01 09:29:35 -0700526 picture.reset(recorder.endRecording());
527 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700528}
529
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000530static void set_canvas_to_save_count_4(SkCanvas* canvas) {
531 canvas->restoreToCount(1);
532 canvas->save();
533 canvas->save();
534 canvas->save();
535}
536
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000537/**
538 * A canvas that records the number of saves, saveLayers and restores.
539 */
540class SaveCountingCanvas : public SkCanvas {
541public:
542 SaveCountingCanvas(int width, int height)
543 : INHERITED(width, height)
544 , fSaveCount(0)
545 , fSaveLayerCount(0)
546 , fRestoreCount(0){
547 }
548
549 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
550 SaveFlags flags) SK_OVERRIDE {
551 ++fSaveLayerCount;
552 return this->INHERITED::willSaveLayer(bounds, paint, flags);
553 }
554
mtklein72c9faa2015-01-09 10:06:39 -0800555 void willSave() SK_OVERRIDE {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000556 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400557 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000558 }
559
mtklein72c9faa2015-01-09 10:06:39 -0800560 void willRestore() SK_OVERRIDE {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000561 ++fRestoreCount;
562 this->INHERITED::willRestore();
563 }
564
565 unsigned int getSaveCount() const { return fSaveCount; }
566 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
567 unsigned int getRestoreCount() const { return fRestoreCount; }
568
569private:
570 unsigned int fSaveCount;
571 unsigned int fSaveLayerCount;
572 unsigned int fRestoreCount;
573
574 typedef SkCanvas INHERITED;
575};
576
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000577void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000578 unsigned int numSaves, unsigned int numSaveLayers,
579 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700580 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700581 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000582
robertphillipsc5ba71d2014-09-04 08:42:50 -0700583 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000584
mtklein87c41382014-09-08 07:31:18 -0700585 // Optimizations may have removed these,
586 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
587 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
588 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
589 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000590}
591
592// This class exists so SkPicture can friend it and give it access to
593// the 'partialReplay' method.
594class SkPictureRecorderReplayTester {
595public:
596 static SkPicture* Copy(SkPictureRecorder* recorder) {
597 SkPictureRecorder recorder2;
598
robertphillips9f1c2412014-06-09 06:25:34 -0700599 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000600
601 recorder->partialReplay(canvas);
602
603 return recorder2.endRecording();
604 }
605};
606
robertphillips9058d602014-06-10 11:45:46 -0700607static void create_imbalance(SkCanvas* canvas) {
608 SkRect clipRect = SkRect::MakeWH(2, 2);
609 SkRect drawRect = SkRect::MakeWH(10, 10);
610 canvas->save();
611 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
612 canvas->translate(1.0f, 1.0f);
613 SkPaint p;
614 p.setColor(SK_ColorGREEN);
615 canvas->drawRect(drawRect, p);
616 // no restore
617}
618
619// This tests that replaying a potentially unbalanced picture into a canvas
620// doesn't affect the canvas' save count or matrix/clip state.
621static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
622 SkBitmap bm;
623 bm.allocN32Pixels(4, 3);
624 SkCanvas canvas(bm);
625
626 int beforeSaveCount = canvas.getSaveCount();
627
628 SkMatrix beforeMatrix = canvas.getTotalMatrix();
629
630 SkRect beforeClip;
631
632 canvas.getClipBounds(&beforeClip);
633
634 canvas.drawPicture(picture);
635
636 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
637 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
638
639 SkRect afterClip;
640
641 canvas.getClipBounds(&afterClip);
642
643 REPORTER_ASSERT(reporter, afterClip == beforeClip);
644}
645
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000646// Test out SkPictureRecorder::partialReplay
647DEF_TEST(PictureRecorder_replay, reporter) {
648 // check save/saveLayer state
649 {
650 SkPictureRecorder recorder;
651
robertphillips9f1c2412014-06-09 06:25:34 -0700652 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000653
654 canvas->saveLayer(NULL, NULL);
655
656 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
657
658 // The extra save and restore comes from the Copy process.
659 check_save_state(reporter, copy, 2, 1, 3);
660
661 canvas->saveLayer(NULL, NULL);
662
663 SkAutoTUnref<SkPicture> final(recorder.endRecording());
664
665 check_save_state(reporter, final, 1, 2, 3);
666
667 // The copy shouldn't pick up any operations added after it was made
668 check_save_state(reporter, copy, 2, 1, 3);
669 }
670
671 // (partially) check leakage of draw ops
672 {
673 SkPictureRecorder recorder;
674
robertphillips9f1c2412014-06-09 06:25:34 -0700675 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000676
677 SkRect r = SkRect::MakeWH(5, 5);
678 SkPaint p;
679
680 canvas->drawRect(r, p);
681
682 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
683
684 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
685
686 SkBitmap bm;
687 make_bm(&bm, 10, 10, SK_ColorRED, true);
688
689 r.offset(5.0f, 5.0f);
690 canvas->drawBitmapRectToRect(bm, NULL, r);
691
692 SkAutoTUnref<SkPicture> final(recorder.endRecording());
693 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
694
695 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
696
697 // The snapshot shouldn't pick up any operations added after it was made
698 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
699 }
robertphillips9058d602014-06-10 11:45:46 -0700700
701 // Recreate the Android partialReplay test case
702 {
703 SkPictureRecorder recorder;
704
705 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
706 create_imbalance(canvas);
707
708 int expectedSaveCount = canvas->getSaveCount();
709
710 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
711 check_balance(reporter, copy);
712
713 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
714
715 // End the recording of source to test the picture finalization
716 // process isn't complicated by the partialReplay step
717 SkAutoTUnref<SkPicture> final(recorder.endRecording());
718 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000719}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000720
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000721static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
722 SkCanvas testCanvas(100, 100);
723 set_canvas_to_save_count_4(&testCanvas);
724
725 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
726
727 SkPaint paint;
728 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
729
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000730 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000731
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000732 {
733 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700734 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000735 canvas->save();
736 canvas->translate(10, 10);
737 canvas->drawRect(rect, paint);
738 canvas->save();
739 canvas->translate(10, 10);
740 canvas->drawRect(rect, paint);
741 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
742
robertphillips9b14f262014-06-04 05:40:44 -0700743 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000744 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
745 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000746
747 set_canvas_to_save_count_4(&testCanvas);
748
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000749 {
750 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700751 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000752 canvas->save();
753 canvas->translate(10, 10);
754 canvas->drawRect(rect, paint);
755 canvas->save();
756 canvas->translate(10, 10);
757 canvas->drawRect(rect, paint);
758 canvas->restore();
759 canvas->restore();
760 canvas->restore();
761 canvas->restore();
762 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000763
robertphillips9b14f262014-06-04 05:40:44 -0700764 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000765 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
766 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000767
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000768 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000769
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000770 {
robertphillips9f1c2412014-06-09 06:25:34 -0700771 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000772 canvas->translate(10, 10);
773 canvas->drawRect(rect, paint);
774 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
775
robertphillips9b14f262014-06-04 05:40:44 -0700776 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000777 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
778 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
779 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000780}
781
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000782static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000783 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000784
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000785 SkPictureRecorder recorder;
786
reed@google.com21b519d2012-10-02 17:42:15 +0000787 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000788 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000789
robertphillips9f1c2412014-06-09 06:25:34 -0700790 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000791
792 for (int i = 0; i < 1000; ++i) {
793 rand_op(canvas, rand);
794 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000795 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000796
797 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000798 }
799
800 {
robertphillips9f1c2412014-06-09 06:25:34 -0700801 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000802 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000803
reed@google.com21b519d2012-10-02 17:42:15 +0000804 for (int i = 0; i < 100; ++i) {
805 canvas->save();
806 }
807 while (canvas->getSaveCount() > 1) {
808 canvas->clipRect(rect);
809 canvas->restore();
810 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000811 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000812 }
813}
814
scroggo@google.com4b90b112012-12-04 15:08:56 +0000815#ifndef SK_DEBUG
816// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
817// should never do this.
818static void test_bad_bitmap() {
819 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
820 // fail.
821 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000822 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000823 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700824 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000825 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000826 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000827
828 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700829 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000830}
831#endif
832
scroggo895c43b2014-12-11 10:53:58 -0800833// Encodes to PNG, unless there is already encoded data, in which case that gets
834// used.
835// FIXME: Share with PictureRenderer.cpp?
836class PngPixelSerializer : public SkPixelSerializer {
837public:
reed53b9e2e2014-12-19 12:26:07 -0800838 bool onUseEncodedData(const void*, size_t) SK_OVERRIDE { return true; }
839 SkData* onEncodePixels(const SkImageInfo& info, const void* pixels,
840 size_t rowBytes) SK_OVERRIDE {
841 return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100);
scroggo895c43b2014-12-11 10:53:58 -0800842 }
843};
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000844
845static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000846 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700847 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700848 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000849 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000850 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
851
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000852 SkDynamicMemoryWStream wStream;
scroggo895c43b2014-12-11 10:53:58 -0800853 PngPixelSerializer serializer;
854 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000855 return wStream.copyToData();
856}
857
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000858struct ErrorContext {
859 int fErrors;
860 skiatest::Reporter* fReporter;
861};
862
863static void assert_one_parse_error_cb(SkError error, void* context) {
864 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
865 errorContext->fErrors++;
866 // This test only expects one error, and that is a kParseError. If there are others,
867 // there is some unknown problem.
868 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
869 "This threw more errors than expected.");
870 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
871 SkGetLastErrorString());
872}
873
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000874static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
875 // Create a bitmap that will be encoded.
876 SkBitmap original;
877 make_bm(&original, 100, 100, SK_ColorBLUE, true);
878 SkDynamicMemoryWStream wStream;
879 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
880 return;
881 }
882 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000883
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000884 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800885 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000886 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000887
888 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
889 // Flattening original will follow the old path of performing an encode, while flattening bm
890 // will use the already encoded data.
891 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
892 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
893 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000894 // Now test that a parse error was generated when trying to create a new SkPicture without
895 // providing a function to decode the bitmap.
896 ErrorContext context;
897 context.fErrors = 0;
898 context.fReporter = reporter;
899 SkSetErrorCallback(assert_one_parse_error_cb, &context);
900 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000901 SkClearLastError();
mtklein08d1fcc2014-11-20 09:18:31 -0800902 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000903 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000904 SkClearLastError();
905 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000906}
907
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000908static void test_clip_bound_opt(skiatest::Reporter* reporter) {
909 // Test for crbug.com/229011
910 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
911 SkIntToScalar(2), SkIntToScalar(2));
912 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
913 SkIntToScalar(1), SkIntToScalar(1));
914 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
915 SkIntToScalar(1), SkIntToScalar(1));
916
917 SkPath invPath;
918 invPath.addOval(rect1);
919 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
920 SkPath path;
921 path.addOval(rect2);
922 SkPath path2;
923 path2.addOval(rect3);
924 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000925 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700926
927 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000928 {
robertphillips9f1c2412014-06-09 06:25:34 -0700929 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000930 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
931 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
932 REPORTER_ASSERT(reporter, true == nonEmpty);
933 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
934 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
935 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
936 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
937 }
938 {
robertphillips9f1c2412014-06-09 06:25:34 -0700939 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000940 canvas->clipPath(path, SkRegion::kIntersect_Op);
941 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
942 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
943 REPORTER_ASSERT(reporter, true == nonEmpty);
944 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
945 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
946 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
947 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
948 }
949 {
robertphillips9f1c2412014-06-09 06:25:34 -0700950 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000951 canvas->clipPath(path, SkRegion::kIntersect_Op);
952 canvas->clipPath(invPath, SkRegion::kUnion_Op);
953 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
954 REPORTER_ASSERT(reporter, true == nonEmpty);
955 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
956 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
957 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
958 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
959 }
960 {
robertphillips9f1c2412014-06-09 06:25:34 -0700961 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000962 canvas->clipPath(path, SkRegion::kDifference_Op);
963 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
964 REPORTER_ASSERT(reporter, true == nonEmpty);
965 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
966 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
967 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
968 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
969 }
970 {
robertphillips9f1c2412014-06-09 06:25:34 -0700971 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000972 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
973 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
974 // True clip is actually empty in this case, but the best
975 // determination we can make using only bounds as input is that the
976 // clip is included in the bounds of 'path'.
977 REPORTER_ASSERT(reporter, true == nonEmpty);
978 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
979 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
980 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
981 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
982 }
983 {
robertphillips9f1c2412014-06-09 06:25:34 -0700984 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000985 canvas->clipPath(path, SkRegion::kIntersect_Op);
986 canvas->clipPath(path2, SkRegion::kXOR_Op);
987 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
988 REPORTER_ASSERT(reporter, true == nonEmpty);
989 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
990 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
991 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
992 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
993 }
994}
995
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000996/**
997 * A canvas that records the number of clip commands.
998 */
999class ClipCountingCanvas : public SkCanvas {
1000public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +00001001 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001002 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001003 , fClipCount(0){
1004 }
1005
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001006 virtual void onClipRect(const SkRect& r,
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::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001011 }
1012
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001013 virtual void onClipRRect(const SkRRect& rrect,
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::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001018 }
1019
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001020 virtual void onClipPath(const SkPath& path,
1021 SkRegion::Op op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001022 ClipEdgeStyle edgeStyle) SK_OVERRIDE {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001023 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001024 this->INHERITED::onClipPath(path, op, edgeStyle);
1025 }
1026
mtklein72c9faa2015-01-09 10:06:39 -08001027 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001028 fClipCount += 1;
1029 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001030 }
1031
1032 unsigned getClipCount() const { return fClipCount; }
1033
1034private:
1035 unsigned fClipCount;
1036
1037 typedef SkCanvas INHERITED;
1038};
1039
1040static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001041 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001042 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001043
1044 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1045 // The following expanding clip should not be skipped.
1046 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1047 // Draw something so the optimizer doesn't just fold the world.
1048 SkPaint p;
1049 p.setColor(SK_ColorBLUE);
1050 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001051 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001052
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001053 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001054 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001055
1056 // Both clips should be present on playback.
1057 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1058}
1059
tomhudson@google.com381010e2013-10-24 11:12:47 +00001060static void test_hierarchical(skiatest::Reporter* reporter) {
1061 SkBitmap bm;
1062 make_bm(&bm, 10, 10, SK_ColorRED, true);
1063
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001064 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001065
robertphillips9f1c2412014-06-09 06:25:34 -07001066 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001067 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1068 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001069
robertphillips9f1c2412014-06-09 06:25:34 -07001070 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001071 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1072 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001073
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001074 {
robertphillips9f1c2412014-06-09 06:25:34 -07001075 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001076 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001077 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1078 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1079 }
1080 {
robertphillips9f1c2412014-06-09 06:25:34 -07001081 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001082 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001083 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1084 REPORTER_ASSERT(reporter, parentPWB->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(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001090 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1091 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1092 }
1093 {
robertphillips9f1c2412014-06-09 06:25:34 -07001094 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001095 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001096 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001097 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1098 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1099 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001100}
1101
robertphillips@google.comd5500882014-04-02 23:51:13 +00001102static void test_gen_id(skiatest::Reporter* reporter) {
1103
Robert Phillipscfaeec42014-07-13 12:00:50 -04001104 SkPictureRecorder recorder;
1105 recorder.beginRecording(0, 0);
1106 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001107
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001108 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001109 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001110
robertphillips9f1c2412014-06-09 06:25:34 -07001111 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001112 canvas->drawARGB(255, 255, 255, 255);
1113 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1114 // picture should have a non-zero id after recording
1115 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001116
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001117 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001118 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001119}
1120
tomhudson158fcaa2014-11-19 10:41:14 -08001121static void test_bytes_used(skiatest::Reporter* reporter) {
1122 SkPictureRecorder recorder;
1123
1124 recorder.beginRecording(0, 0);
1125 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1126
1127 // Sanity check to make sure we aren't under-measuring.
1128 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >=
1129 sizeof(SkPicture) + sizeof(SkRecord));
1130
1131 // Protect against any unintentional bloat.
reed1bdfd3f2014-11-24 14:41:51 -08001132 size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
1133 REPORTER_ASSERT(reporter, approxUsed <= 136);
tomhudson158fcaa2014-11-19 10:41:14 -08001134
1135 // Sanity check of nested SkPictures.
1136 SkPictureRecorder r2;
1137 r2.beginRecording(0, 0);
1138 r2.getRecordingCanvas()->drawPicture(empty.get());
1139 SkAutoTUnref<SkPicture> nested(r2.endRecording());
1140
1141 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >
1142 SkPictureUtils::ApproximateBytesUsed(empty.get()));
1143}
1144
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001145DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001146#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001147 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001148 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001149#else
1150 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001151#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001152 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001153 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001154#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001155 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001156#endif
mtklein8e126562014-10-01 09:29:35 -07001157 test_has_text(reporter);
1158 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001159 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001160 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001161 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001162 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001163 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001164 test_savelayer_extraction(reporter);
tomhudson158fcaa2014-11-19 10:41:14 -08001165 test_bytes_used(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001166}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001167
1168static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1169 const SkPaint paint;
1170 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1171 const SkIRect irect = { 2, 2, 3, 3 };
1172
1173 // Don't care what these record, as long as they're legal.
1174 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1175 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001176 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1177 canvas->drawSprite(bitmap, 1, 1);
1178}
1179
1180static void test_draw_bitmaps(SkCanvas* canvas) {
1181 SkBitmap empty;
1182 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001183 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001184 draw_bitmaps(empty, canvas);
1185}
1186
1187DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001188 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001189 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001190 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001191}
1192
1193DEF_TEST(Canvas_EmptyBitmap, r) {
1194 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001195 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001196 SkCanvas canvas(dst);
1197
1198 test_draw_bitmaps(&canvas);
1199}
dneto3f22e8c2014-07-30 15:42:22 -07001200
1201DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1202 // This test is from crbug.com/344987.
1203 // The commands are:
1204 // saveLayer with paint that modifies alpha
1205 // drawBitmapRectToRect
1206 // drawBitmapRectToRect
1207 // restore
1208 // The bug was that this structure was modified so that:
1209 // - The saveLayer and restore were eliminated
1210 // - The alpha was only applied to the first drawBitmapRectToRect
1211
1212 // This test draws blue and red squares inside a 50% transparent
1213 // layer. Both colours should show up muted.
1214 // When the bug is present, the red square (the second bitmap)
1215 // shows upwith full opacity.
1216
1217 SkBitmap blueBM;
1218 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1219 SkBitmap redBM;
1220 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1221 SkPaint semiTransparent;
1222 semiTransparent.setAlpha(0x80);
1223
1224 SkPictureRecorder recorder;
1225 SkCanvas* canvas = recorder.beginRecording(100, 100);
1226 canvas->drawARGB(0, 0, 0, 0);
1227
1228 canvas->saveLayer(0, &semiTransparent);
1229 canvas->drawBitmap(blueBM, 25, 25);
1230 canvas->drawBitmap(redBM, 50, 50);
1231 canvas->restore();
1232
1233 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1234
1235 // Now replay the picture back on another canvas
1236 // and check a couple of its pixels.
1237 SkBitmap replayBM;
1238 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1239 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001240 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001241 replayCanvas.flush();
1242
1243 // With the bug present, at (55, 55) we would get a fully opaque red
1244 // intead of a dark red.
1245 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1246 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1247}
mtklein3e8232b2014-08-18 13:39:11 -07001248
1249struct CountingBBH : public SkBBoxHierarchy {
1250 mutable int searchCalls;
1251
1252 CountingBBH() : searchCalls(0) {}
1253
mtklein72c9faa2015-01-09 10:06:39 -08001254 void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {
mtklein3e8232b2014-08-18 13:39:11 -07001255 this->searchCalls++;
1256 }
1257
mtkleinbfd5bff2015-02-10 13:44:27 -08001258 void insert(const SkRect[], int) SK_OVERRIDE {}
rmistry465206a2015-02-02 12:08:18 -08001259 virtual size_t bytesUsed() const SK_OVERRIDE { return 0; }
mtklein3e8232b2014-08-18 13:39:11 -07001260};
1261
1262class SpoonFedBBHFactory : public SkBBHFactory {
1263public:
1264 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
reed78e27682014-11-19 08:04:34 -08001265 SkBBoxHierarchy* operator()(const SkRect&) const SK_OVERRIDE {
mtklein3e8232b2014-08-18 13:39:11 -07001266 return SkRef(fBBH);
1267 }
1268private:
1269 SkBBoxHierarchy* fBBH;
1270};
1271
1272// When the canvas clip covers the full picture, we don't need to call the BBH.
1273DEF_TEST(Picture_SkipBBH, r) {
1274 CountingBBH bbh;
1275 SpoonFedBBHFactory factory(&bbh);
1276
1277 SkPictureRecorder recorder;
1278 recorder.beginRecording(320, 240, &factory);
1279 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1280
1281 SkCanvas big(640, 480), small(300, 200);
1282
robertphillipsc5ba71d2014-09-04 08:42:50 -07001283 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001284 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1285
robertphillipsc5ba71d2014-09-04 08:42:50 -07001286 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001287 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1288}
mtkleind72094d2014-08-27 12:12:23 -07001289
1290DEF_TEST(Picture_BitmapLeak, r) {
1291 SkBitmap mut, immut;
1292 mut.allocN32Pixels(300, 200);
1293 immut.allocN32Pixels(300, 200);
1294 immut.setImmutable();
1295 SkASSERT(!mut.isImmutable());
1296 SkASSERT(immut.isImmutable());
1297
1298 // No one can hold a ref on our pixels yet.
1299 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1300 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1301
reed1bdfd3f2014-11-24 14:41:51 -08001302 SkAutoTUnref<const SkPicture> pic;
1303 {
1304 // we want the recorder to go out of scope before our subsequent checks, so we
1305 // place it inside local braces.
1306 SkPictureRecorder rec;
1307 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1308 canvas->drawBitmap(mut, 0, 0);
1309 canvas->drawBitmap(immut, 800, 600);
1310 pic.reset(rec.endRecording());
1311 }
mtkleind72094d2014-08-27 12:12:23 -07001312
1313 // The picture shares the immutable pixels but copies the mutable ones.
1314 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1315 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1316
1317 // When the picture goes away, it's just our bitmaps holding the refs.
1318 pic.reset(NULL);
1319 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1320 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1321}