blob: 0ccef3f0a92f2b8403278fc3cdd7326349001847 [file] [log] [blame]
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
mtklein3e8232b2014-08-18 13:39:11 -07008#include "SkBBoxHierarchy.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +00009#include "SkBlurImageFilter.h"
reed@google.com21b519d2012-10-02 17:42:15 +000010#include "SkCanvas.h"
robertphillipsd8aa7b72014-10-30 16:45:02 -070011#include "SkColorMatrixFilter.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000012#include "SkColorPriv.h"
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +000013#include "SkDashPathEffect.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000014#include "SkData.h"
reed5965c8a2015-01-07 18:04:45 -080015#include "SkImageGenerator.h"
scroggo@google.com49ce11b2013-04-25 18:29:32 +000016#include "SkError.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000017#include "SkImageEncoder.h"
18#include "SkImageGenerator.h"
robertphillips82365912014-11-12 09:32:34 -080019#include "SkLayerInfo.h"
reed@google.com21b519d2012-10-02 17:42:15 +000020#include "SkPaint.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000021#include "SkPicture.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000022#include "SkPictureRecorder.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000023#include "SkPictureUtils.h"
mtkleind72094d2014-08-27 12:12:23 -070024#include "SkPixelRef.h"
scroggo895c43b2014-12-11 10:53:58 -080025#include "SkPixelSerializer.h"
mtklein9db912c2015-05-19 11:11:26 -070026#include "SkMiniRecorder.h"
reed@google.com72aa79c2013-01-24 18:27:42 +000027#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000028#include "SkRandom.h"
tomhudson158fcaa2014-11-19 10:41:14 -080029#include "SkRecord.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000030#include "SkShader.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000031#include "SkStream.h"
robertphillips3e5c2b12015-03-23 05:46:51 -070032#include "sk_tool_utils.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000033
34#if SK_SUPPORT_GPU
35#include "SkSurface.h"
36#include "GrContextFactory.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000037#endif
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000038#include "Test.h"
scroggo@google.comd614c6a2012-09-14 17:26:37 +000039
reed@google.com47b679b2014-05-14 18:58:16 +000040#include "SkLumaColorFilter.h"
41#include "SkColorFilterImageFilter.h"
42
reed@google.comfe7b1ed2012-11-29 21:00:39 +000043static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +000044 bm->allocN32Pixels(w, h);
reed@google.comfe7b1ed2012-11-29 21:00:39 +000045 bm->eraseColor(color);
46 if (immutable) {
47 bm->setImmutable();
48 }
49}
50
tomhudson3a0f2792014-08-20 05:29:41 -070051/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
mtklein8e126562014-10-01 09:29:35 -070052static void test_analysis(skiatest::Reporter* reporter) {
tomhudson3a0f2792014-08-20 05:29:41 -070053 SkPictureRecorder recorder;
54
mtklein8e126562014-10-01 09:29:35 -070055 SkCanvas* canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070056 {
57 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
58 }
59 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
60 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
61
mtklein8e126562014-10-01 09:29:35 -070062 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -070063 {
64 SkPaint paint;
65 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
66 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
67 SkBitmap bitmap;
68 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
69 bitmap.eraseColor(SK_ColorBLUE);
70 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
71 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
72 SkShader::kClamp_TileMode);
73 paint.setShader(shader)->unref();
74 REPORTER_ASSERT(reporter,
75 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
76
77 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
78 }
79 picture.reset(recorder.endRecording());
80 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
81}
82
83
scroggo@google.comd614c6a2012-09-14 17:26:37 +000084#ifdef SK_DEBUG
mtklein3e8232b2014-08-18 13:39:11 -070085// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
robertphillipsdb539902014-07-01 08:47:04 -070086// in debug mode, so only run in debug mode.
87static void test_deleting_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000088 SkPictureRecorder recorder;
scroggo@google.comd614c6a2012-09-14 17:26:37 +000089 // Creates an SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070090 recorder.beginRecording(0, 0);
robertphillipsdb539902014-07-01 08:47:04 -070091 // Turns that into an SkPicture
robertphillips@google.com84b18c72014-04-13 19:09:42 +000092 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
robertphillipsdb539902014-07-01 08:47:04 -070093 // Ceates a new SkPictureRecord
robertphillips9f1c2412014-06-09 06:25:34 -070094 recorder.beginRecording(0, 0);
scroggo@google.comd614c6a2012-09-14 17:26:37 +000095}
96
97// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
98static void test_serializing_empty_picture() {
robertphillips@google.com84b18c72014-04-13 19:09:42 +000099 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700100 recorder.beginRecording(0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000101 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000102 SkDynamicMemoryWStream stream;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000103 picture->serialize(&stream);
scroggo@google.comd614c6a2012-09-14 17:26:37 +0000104}
105#endif
106
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000107static void rand_op(SkCanvas* canvas, SkRandom& rand) {
reed@google.com21b519d2012-10-02 17:42:15 +0000108 SkPaint paint;
109 SkRect rect = SkRect::MakeWH(50, 50);
110
111 SkScalar unit = rand.nextUScalar1();
112 if (unit <= 0.3) {
113// SkDebugf("save\n");
114 canvas->save();
115 } else if (unit <= 0.6) {
116// SkDebugf("restore\n");
117 canvas->restore();
118 } else if (unit <= 0.9) {
119// SkDebugf("clip\n");
120 canvas->clipRect(rect);
121 } else {
122// SkDebugf("draw\n");
123 canvas->drawPaint(paint);
124 }
125}
126
robertphillips@google.comb950c6f2014-04-25 00:02:12 +0000127#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700128
mtklein8e126562014-10-01 09:29:35 -0700129static void test_gpu_veto(skiatest::Reporter* reporter) {
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000130 SkPictureRecorder recorder;
131
mtklein8e126562014-10-01 09:29:35 -0700132 SkCanvas* canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000133 {
134 SkPath path;
135 path.moveTo(0, 0);
136 path.lineTo(50, 50);
137
138 SkScalar intervals[] = { 1.0f, 1.0f };
139 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
140
141 SkPaint paint;
142 paint.setStyle(SkPaint::kStroke_Style);
143 paint.setPathEffect(dash);
144
robertphillips98b03152015-01-26 11:29:36 -0800145 for (int i = 0; i < 50; ++i) {
146 canvas->drawPath(path, paint);
147 }
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000148 }
149 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
150 // path effects currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000151
152 const char *reason = NULL;
153 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
bsalomon49f085d2014-09-05 13:34:00 -0700154 REPORTER_ASSERT(reporter, reason);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000155
mtklein8e126562014-10-01 09:29:35 -0700156 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000157 {
158 SkPath path;
159
160 path.moveTo(0, 0);
161 path.lineTo(0, 50);
162 path.lineTo(25, 25);
163 path.lineTo(50, 50);
164 path.lineTo(50, 0);
165 path.close();
166 REPORTER_ASSERT(reporter, !path.isConvex());
167
168 SkPaint paint;
169 paint.setAntiAlias(true);
170 for (int i = 0; i < 50; ++i) {
171 canvas->drawPath(path, paint);
172 }
173 }
174 picture.reset(recorder.endRecording());
jvanverthd86b07a2014-11-04 08:50:15 -0800175 // A lot of small AA concave paths should be fine for GPU rendering
176 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
177
178 canvas = recorder.beginRecording(100, 100);
179 {
180 SkPath path;
181
182 path.moveTo(0, 0);
183 path.lineTo(0, 100);
184 path.lineTo(50, 50);
185 path.lineTo(100, 100);
186 path.lineTo(100, 0);
187 path.close();
188 REPORTER_ASSERT(reporter, !path.isConvex());
189
190 SkPaint paint;
191 paint.setAntiAlias(true);
192 for (int i = 0; i < 50; ++i) {
193 canvas->drawPath(path, paint);
194 }
195 }
196 picture.reset(recorder.endRecording());
197 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000198 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
199
mtklein8e126562014-10-01 09:29:35 -0700200 canvas = recorder.beginRecording(100, 100);
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000201 {
202 SkPath path;
203
204 path.moveTo(0, 0);
205 path.lineTo(0, 50);
206 path.lineTo(25, 25);
207 path.lineTo(50, 50);
208 path.lineTo(50, 0);
209 path.close();
210 REPORTER_ASSERT(reporter, !path.isConvex());
211
212 SkPaint paint;
213 paint.setAntiAlias(true);
214 paint.setStyle(SkPaint::kStroke_Style);
215 paint.setStrokeWidth(0);
216 for (int i = 0; i < 50; ++i) {
217 canvas->drawPath(path, paint);
218 }
219 }
220 picture.reset(recorder.endRecording());
221 // hairline stroked AA concave paths are fine for GPU rendering
222 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
tomhudson3a0f2792014-08-20 05:29:41 -0700223
mtklein8e126562014-10-01 09:29:35 -0700224 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700225 {
226 SkPaint paint;
227 SkScalar intervals [] = { 10, 20 };
228 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
229 paint.setPathEffect(pe)->unref();
230
231 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
robertphillips98b03152015-01-26 11:29:36 -0800232
233 for (int i = 0; i < 50; ++i) {
234 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
235 }
tomhudson3a0f2792014-08-20 05:29:41 -0700236 }
237 picture.reset(recorder.endRecording());
238 // fast-path dashed effects are fine for GPU rendering ...
239 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
240
mtklein8e126562014-10-01 09:29:35 -0700241 canvas = recorder.beginRecording(100, 100);
tomhudson3a0f2792014-08-20 05:29:41 -0700242 {
243 SkPaint paint;
244 SkScalar intervals [] = { 10, 20 };
245 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
246 paint.setPathEffect(pe)->unref();
247
robertphillips98b03152015-01-26 11:29:36 -0800248 for (int i = 0; i < 50; ++i) {
249 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
250 }
tomhudson3a0f2792014-08-20 05:29:41 -0700251 }
252 picture.reset(recorder.endRecording());
253 // ... but only when applied to drawPoint() calls
254 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
mtklein53fecfb2014-08-21 09:11:37 -0700255
256 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700257 canvas = recorder.beginRecording(100, 100);
258 {
259 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700260 }
mtklein8e126562014-10-01 09:29:35 -0700261 picture.reset(recorder.endRecording());
262 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000263}
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000264
robertphillips82365912014-11-12 09:32:34 -0800265#endif
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000266
robertphillips82365912014-11-12 09:32:34 -0800267static void test_savelayer_extraction(skiatest::Reporter* reporter) {
268 static const int kWidth = 100;
269 static const int kHeight = 100;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000270
robertphillips82365912014-11-12 09:32:34 -0800271 // Create complex paint that the bounding box computation code can't
272 // optimize away
273 SkScalar blueToRedMatrix[20] = { 0 };
274 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
275 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
276 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000277
robertphillips82365912014-11-12 09:32:34 -0800278 SkPaint complexPaint;
279 complexPaint.setImageFilter(filter);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000280
robertphillips82365912014-11-12 09:32:34 -0800281 SkAutoTUnref<SkPicture> pict, child;
282 SkRTreeFactory bbhFactory;
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000283
robertphillips82365912014-11-12 09:32:34 -0800284 {
285 SkPictureRecorder recorder;
robertphillipsd8aa7b72014-10-30 16:45:02 -0700286
robertphillips82365912014-11-12 09:32:34 -0800287 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
288 &bbhFactory,
289 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
robertphillipsd8aa7b72014-10-30 16:45:02 -0700290
robertphillips82365912014-11-12 09:32:34 -0800291 c->saveLayer(NULL, &complexPaint);
292 c->restore();
robertphillipsd6283302014-08-27 11:53:28 -0700293
robertphillips82365912014-11-12 09:32:34 -0800294 child.reset(recorder.endRecording());
295 }
robertphillipsd6283302014-08-27 11:53:28 -0700296
robertphillips82365912014-11-12 09:32:34 -0800297 // create a picture with the structure:
298 // 1)
299 // SaveLayer
300 // Restore
301 // 2)
302 // SaveLayer
303 // Translate
304 // SaveLayer w/ bound
305 // Restore
306 // Restore
307 // 3)
308 // SaveLayer w/ copyable paint
309 // Restore
310 // 4)
311 // SaveLayer
312 // DrawPicture (which has a SaveLayer/Restore pair)
313 // Restore
314 // 5)
315 // SaveLayer
316 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
317 // Restore
318 {
319 SkPictureRecorder recorder;
robertphillipsd6283302014-08-27 11:53:28 -0700320
robertphillips82365912014-11-12 09:32:34 -0800321 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
322 SkIntToScalar(kHeight),
323 &bbhFactory,
324 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000325 // 1)
robertphillips82365912014-11-12 09:32:34 -0800326 c->saveLayer(NULL, &complexPaint); // layer #0
327 c->restore();
328
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000329 // 2)
robertphillips82365912014-11-12 09:32:34 -0800330 c->saveLayer(NULL, NULL); // layer #1
robertphillips01d6e5f2014-12-01 09:09:27 -0800331 c->translate(kWidth / 2.0f, kHeight / 2.0f);
robertphillips82365912014-11-12 09:32:34 -0800332 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
333 c->saveLayer(&r, &complexPaint); // layer #2
334 c->restore();
335 c->restore();
336
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000337 // 3)
robertphillips82365912014-11-12 09:32:34 -0800338 {
339 c->saveLayer(NULL, &complexPaint); // layer #3
340 c->restore();
341 }
342
343 SkPaint layerPaint;
344 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000345 // 4)
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000346 {
robertphillips82365912014-11-12 09:32:34 -0800347 c->saveLayer(NULL, &layerPaint); // layer #4
348 c->drawPicture(child); // layer #5 inside picture
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000349 c->restore();
robertphillips82365912014-11-12 09:32:34 -0800350 }
351 // 5
352 {
353 SkPaint picturePaint;
354 SkMatrix trans;
355 trans.setTranslate(10, 10);
bsalomone904c092014-07-17 10:50:59 -0700356
robertphillips82365912014-11-12 09:32:34 -0800357 c->saveLayer(NULL, &layerPaint); // layer #6
358 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
bsalomone904c092014-07-17 10:50:59 -0700359 c->restore();
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000360 }
361
robertphillips82365912014-11-12 09:32:34 -0800362 pict.reset(recorder.endRecording());
363 }
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000364
robertphillips82365912014-11-12 09:32:34 -0800365 // Now test out the SaveLayer extraction
reedd990e2f2014-12-22 11:58:30 -0800366 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
mtklein9db912c2015-05-19 11:11:26 -0700367 const SkBigPicture* bp = pict->asSkBigPicture();
368 REPORTER_ASSERT(reporter, bp);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000369
mtklein9db912c2015-05-19 11:11:26 -0700370 const SkBigPicture::AccelData* data = bp->accelData();
robertphillips82365912014-11-12 09:32:34 -0800371 REPORTER_ASSERT(reporter, data);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000372
robertphillips82365912014-11-12 09:32:34 -0800373 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
374 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
robertphillipsd6283302014-08-27 11:53:28 -0700375
robertphillips82365912014-11-12 09:32:34 -0800376 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
377 // The parent/child layers appear in reverse order
378 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
379 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
robertphillipsd6283302014-08-27 11:53:28 -0700380
robertphillips82365912014-11-12 09:32:34 -0800381 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
robertphillipsd6283302014-08-27 11:53:28 -0700382
robertphillips82365912014-11-12 09:32:34 -0800383 // The parent/child layers appear in reverse order
384 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
385 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000386
robertphillips82365912014-11-12 09:32:34 -0800387 // The parent/child layers appear in reverse order
388 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
389 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000390
robertphillips82365912014-11-12 09:32:34 -0800391 REPORTER_ASSERT(reporter, NULL == info0.fPicture);
392 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
393 kHeight == info0.fBounds.height());
394 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
395 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
396 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
397 REPORTER_ASSERT(reporter, NULL != info0.fPaint);
398 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000399
robertphillips82365912014-11-12 09:32:34 -0800400 REPORTER_ASSERT(reporter, NULL == info1.fPicture);
401 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
402 kHeight/2.0 == info1.fBounds.height());
403 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
404 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
mtklein04c96952014-11-24 08:20:57 -0800405 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
robertphillips82365912014-11-12 09:32:34 -0800406 kHeight/2.0 == info1.fBounds.fTop);
407 REPORTER_ASSERT(reporter, NULL == info1.fPaint);
408 REPORTER_ASSERT(reporter, !info1.fIsNested &&
409 info1.fHasNestedLayers); // has a nested SL
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000410
robertphillips82365912014-11-12 09:32:34 -0800411 REPORTER_ASSERT(reporter, NULL == info2.fPicture);
412 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
413 kHeight / 2 == info2.fBounds.height()); // bound reduces size
414 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
415 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
416 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
417 kHeight / 2 == info2.fBounds.fTop);
418 REPORTER_ASSERT(reporter, NULL != info2.fPaint);
419 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000420
robertphillips82365912014-11-12 09:32:34 -0800421 REPORTER_ASSERT(reporter, NULL == info3.fPicture);
422 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
423 kHeight == info3.fBounds.height());
424 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
425 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
426 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
427 REPORTER_ASSERT(reporter, info3.fPaint);
428 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
robertphillipsd6283302014-08-27 11:53:28 -0700429
robertphillips82365912014-11-12 09:32:34 -0800430 REPORTER_ASSERT(reporter, NULL == info4.fPicture);
431 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
432 kHeight == info4.fBounds.height());
433 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
434 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
435 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
436 REPORTER_ASSERT(reporter, info4.fPaint);
437 REPORTER_ASSERT(reporter, !info4.fIsNested &&
438 info4.fHasNestedLayers); // has a nested SL
robertphillipsd6283302014-08-27 11:53:28 -0700439
robertphillips82365912014-11-12 09:32:34 -0800440 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
441 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
442 kHeight == info5.fBounds.height());
443 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
444 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
445 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
446 REPORTER_ASSERT(reporter, NULL != info5.fPaint);
447 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
robertphillipsd6283302014-08-27 11:53:28 -0700448
robertphillips82365912014-11-12 09:32:34 -0800449 REPORTER_ASSERT(reporter, NULL == info6.fPicture);
450 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
451 kHeight-10 == info6.fBounds.height());
452 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
453 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
454 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
455 REPORTER_ASSERT(reporter, info6.fPaint);
456 REPORTER_ASSERT(reporter, !info6.fIsNested &&
457 info6.fHasNestedLayers); // has a nested SL
458
459 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
460 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
461 kHeight == info7.fBounds.height());
462 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
463 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
464 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
465 REPORTER_ASSERT(reporter, NULL != info7.fPaint);
466 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +0000467 }
468}
469
mtklein8e126562014-10-01 09:29:35 -0700470static void test_has_text(skiatest::Reporter* reporter) {
ajuma750ae262014-08-18 12:59:55 -0700471 SkPictureRecorder recorder;
ajuma750ae262014-08-18 12:59:55 -0700472
mtklein8e126562014-10-01 09:29:35 -0700473 SkCanvas* canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700474 {
mtkleinc551d9f2014-08-20 08:09:46 -0700475 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700476 }
477 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
478 REPORTER_ASSERT(reporter, !picture->hasText());
479
mtkleinc551d9f2014-08-20 08:09:46 -0700480 SkPoint point = SkPoint::Make(10, 10);
mtklein8e126562014-10-01 09:29:35 -0700481 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700482 {
mtkleinc551d9f2014-08-20 08:09:46 -0700483 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700484 }
485 picture.reset(recorder.endRecording());
486 REPORTER_ASSERT(reporter, picture->hasText());
487
mtklein8e126562014-10-01 09:29:35 -0700488 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700489 {
mtkleinc551d9f2014-08-20 08:09:46 -0700490 canvas->drawPosText("Q", 1, &point, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700491 }
492 picture.reset(recorder.endRecording());
493 REPORTER_ASSERT(reporter, picture->hasText());
494
mtklein8e126562014-10-01 09:29:35 -0700495 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700496 {
mtkleinc551d9f2014-08-20 08:09:46 -0700497 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700498 }
499 picture.reset(recorder.endRecording());
500 REPORTER_ASSERT(reporter, picture->hasText());
501
mtklein8e126562014-10-01 09:29:35 -0700502 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700503 {
504 SkPath path;
505 path.moveTo(0, 0);
506 path.lineTo(50, 50);
507
mtkleinc551d9f2014-08-20 08:09:46 -0700508 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700509 }
510 picture.reset(recorder.endRecording());
511 REPORTER_ASSERT(reporter, picture->hasText());
512
mtklein8e126562014-10-01 09:29:35 -0700513 canvas = recorder.beginRecording(100,100);
ajuma750ae262014-08-18 12:59:55 -0700514 {
515 SkPath path;
516 path.moveTo(0, 0);
517 path.lineTo(50, 50);
518
mtkleinc551d9f2014-08-20 08:09:46 -0700519 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint());
ajuma750ae262014-08-18 12:59:55 -0700520 }
521 picture.reset(recorder.endRecording());
522 REPORTER_ASSERT(reporter, picture->hasText());
mtklein53fecfb2014-08-21 09:11:37 -0700523
524 // Nest the previous picture inside a new one.
mtklein8e126562014-10-01 09:29:35 -0700525 canvas = recorder.beginRecording(100,100);
526 {
527 canvas->drawPicture(picture.get());
mtklein53fecfb2014-08-21 09:11:37 -0700528 }
mtklein8e126562014-10-01 09:29:35 -0700529 picture.reset(recorder.endRecording());
530 REPORTER_ASSERT(reporter, picture->hasText());
ajuma750ae262014-08-18 12:59:55 -0700531}
532
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000533static void set_canvas_to_save_count_4(SkCanvas* canvas) {
534 canvas->restoreToCount(1);
535 canvas->save();
536 canvas->save();
537 canvas->save();
538}
539
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000540/**
541 * A canvas that records the number of saves, saveLayers and restores.
542 */
543class SaveCountingCanvas : public SkCanvas {
544public:
545 SaveCountingCanvas(int width, int height)
546 : INHERITED(width, height)
547 , fSaveCount(0)
548 , fSaveLayerCount(0)
549 , fRestoreCount(0){
550 }
551
552 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
mtklein36352bf2015-03-25 18:17:31 -0700553 SaveFlags flags) override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000554 ++fSaveLayerCount;
555 return this->INHERITED::willSaveLayer(bounds, paint, flags);
556 }
557
mtklein36352bf2015-03-25 18:17:31 -0700558 void willSave() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000559 ++fSaveCount;
Florin Malita5f6102d2014-06-30 10:13:28 -0400560 this->INHERITED::willSave();
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000561 }
562
mtklein36352bf2015-03-25 18:17:31 -0700563 void willRestore() override {
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000564 ++fRestoreCount;
565 this->INHERITED::willRestore();
566 }
567
568 unsigned int getSaveCount() const { return fSaveCount; }
569 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
570 unsigned int getRestoreCount() const { return fRestoreCount; }
571
572private:
573 unsigned int fSaveCount;
574 unsigned int fSaveLayerCount;
575 unsigned int fRestoreCount;
576
577 typedef SkCanvas INHERITED;
578};
579
skia.committer@gmail.com8e7d37d2014-05-28 03:06:06 +0000580void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000581 unsigned int numSaves, unsigned int numSaveLayers,
582 unsigned int numRestores) {
mtklein87c41382014-09-08 07:31:18 -0700583 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700584 SkScalarCeilToInt(picture->cullRect().height()));
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000585
robertphillipsc5ba71d2014-09-04 08:42:50 -0700586 picture->playback(&canvas);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000587
mtklein87c41382014-09-08 07:31:18 -0700588 // Optimizations may have removed these,
589 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
590 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
591 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
592 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000593}
594
595// This class exists so SkPicture can friend it and give it access to
596// the 'partialReplay' method.
597class SkPictureRecorderReplayTester {
598public:
599 static SkPicture* Copy(SkPictureRecorder* recorder) {
600 SkPictureRecorder recorder2;
601
robertphillips9f1c2412014-06-09 06:25:34 -0700602 SkCanvas* canvas = recorder2.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000603
604 recorder->partialReplay(canvas);
605
606 return recorder2.endRecording();
607 }
608};
609
robertphillips9058d602014-06-10 11:45:46 -0700610static void create_imbalance(SkCanvas* canvas) {
611 SkRect clipRect = SkRect::MakeWH(2, 2);
612 SkRect drawRect = SkRect::MakeWH(10, 10);
613 canvas->save();
614 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
615 canvas->translate(1.0f, 1.0f);
616 SkPaint p;
617 p.setColor(SK_ColorGREEN);
618 canvas->drawRect(drawRect, p);
619 // no restore
620}
621
622// This tests that replaying a potentially unbalanced picture into a canvas
623// doesn't affect the canvas' save count or matrix/clip state.
624static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
625 SkBitmap bm;
626 bm.allocN32Pixels(4, 3);
627 SkCanvas canvas(bm);
628
629 int beforeSaveCount = canvas.getSaveCount();
630
631 SkMatrix beforeMatrix = canvas.getTotalMatrix();
632
633 SkRect beforeClip;
634
635 canvas.getClipBounds(&beforeClip);
636
637 canvas.drawPicture(picture);
638
639 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
640 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
641
642 SkRect afterClip;
643
644 canvas.getClipBounds(&afterClip);
645
646 REPORTER_ASSERT(reporter, afterClip == beforeClip);
647}
648
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000649// Test out SkPictureRecorder::partialReplay
650DEF_TEST(PictureRecorder_replay, reporter) {
651 // check save/saveLayer state
652 {
653 SkPictureRecorder recorder;
654
robertphillips9f1c2412014-06-09 06:25:34 -0700655 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000656
657 canvas->saveLayer(NULL, NULL);
658
659 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
660
661 // The extra save and restore comes from the Copy process.
662 check_save_state(reporter, copy, 2, 1, 3);
663
664 canvas->saveLayer(NULL, NULL);
665
666 SkAutoTUnref<SkPicture> final(recorder.endRecording());
667
668 check_save_state(reporter, final, 1, 2, 3);
669
670 // The copy shouldn't pick up any operations added after it was made
671 check_save_state(reporter, copy, 2, 1, 3);
672 }
673
674 // (partially) check leakage of draw ops
675 {
676 SkPictureRecorder recorder;
677
robertphillips9f1c2412014-06-09 06:25:34 -0700678 SkCanvas* canvas = recorder.beginRecording(10, 10);
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000679
680 SkRect r = SkRect::MakeWH(5, 5);
681 SkPaint p;
682
683 canvas->drawRect(r, p);
684
685 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
686
687 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
688
689 SkBitmap bm;
690 make_bm(&bm, 10, 10, SK_ColorRED, true);
691
692 r.offset(5.0f, 5.0f);
693 canvas->drawBitmapRectToRect(bm, NULL, r);
694
695 SkAutoTUnref<SkPicture> final(recorder.endRecording());
696 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
697
698 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
699
700 // The snapshot shouldn't pick up any operations added after it was made
701 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
702 }
robertphillips9058d602014-06-10 11:45:46 -0700703
704 // Recreate the Android partialReplay test case
705 {
706 SkPictureRecorder recorder;
707
708 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
709 create_imbalance(canvas);
710
711 int expectedSaveCount = canvas->getSaveCount();
712
713 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
714 check_balance(reporter, copy);
715
716 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
717
718 // End the recording of source to test the picture finalization
719 // process isn't complicated by the partialReplay step
720 SkAutoTUnref<SkPicture> final(recorder.endRecording());
721 }
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000722}
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000723
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000724static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
725 SkCanvas testCanvas(100, 100);
726 set_canvas_to_save_count_4(&testCanvas);
727
728 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
729
730 SkPaint paint;
731 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
732
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000733 SkPictureRecorder recorder;
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000734
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000735 {
736 // Create picture with 2 unbalanced saves
robertphillips9f1c2412014-06-09 06:25:34 -0700737 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000738 canvas->save();
739 canvas->translate(10, 10);
740 canvas->drawRect(rect, paint);
741 canvas->save();
742 canvas->translate(10, 10);
743 canvas->drawRect(rect, paint);
744 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
745
robertphillips9b14f262014-06-04 05:40:44 -0700746 testCanvas.drawPicture(extraSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000747 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
748 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000749
750 set_canvas_to_save_count_4(&testCanvas);
751
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000752 {
753 // Create picture with 2 unbalanced restores
robertphillips9f1c2412014-06-09 06:25:34 -0700754 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000755 canvas->save();
756 canvas->translate(10, 10);
757 canvas->drawRect(rect, paint);
758 canvas->save();
759 canvas->translate(10, 10);
760 canvas->drawRect(rect, paint);
761 canvas->restore();
762 canvas->restore();
763 canvas->restore();
764 canvas->restore();
765 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000766
robertphillips9b14f262014-06-04 05:40:44 -0700767 testCanvas.drawPicture(extraRestorePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000768 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
769 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000770
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000771 set_canvas_to_save_count_4(&testCanvas);
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000772
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000773 {
robertphillips9f1c2412014-06-09 06:25:34 -0700774 SkCanvas* canvas = recorder.beginRecording(100, 100);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000775 canvas->translate(10, 10);
776 canvas->drawRect(rect, paint);
777 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
778
robertphillips9b14f262014-06-04 05:40:44 -0700779 testCanvas.drawPicture(noSavePicture);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000780 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
781 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
782 }
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +0000783}
784
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000785static void test_peephole() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000786 SkRandom rand;
reed@google.com21b519d2012-10-02 17:42:15 +0000787
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000788 SkPictureRecorder recorder;
789
reed@google.com21b519d2012-10-02 17:42:15 +0000790 for (int j = 0; j < 100; j++) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000791 SkRandom rand2(rand); // remember the seed
reed@google.com21b519d2012-10-02 17:42:15 +0000792
robertphillips9f1c2412014-06-09 06:25:34 -0700793 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000794
795 for (int i = 0; i < 1000; ++i) {
796 rand_op(canvas, rand);
797 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000798 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
jvanverth@google.comc490f802013-03-04 13:56:38 +0000799
800 rand = rand2;
reed@google.com21b519d2012-10-02 17:42:15 +0000801 }
802
803 {
robertphillips9f1c2412014-06-09 06:25:34 -0700804 SkCanvas* canvas = recorder.beginRecording(100, 100);
reed@google.com21b519d2012-10-02 17:42:15 +0000805 SkRect rect = SkRect::MakeWH(50, 50);
skia.committer@gmail.com52c24372012-10-03 02:01:13 +0000806
reed@google.com21b519d2012-10-02 17:42:15 +0000807 for (int i = 0; i < 100; ++i) {
808 canvas->save();
809 }
810 while (canvas->getSaveCount() > 1) {
811 canvas->clipRect(rect);
812 canvas->restore();
813 }
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000814 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
reed@google.com21b519d2012-10-02 17:42:15 +0000815 }
816}
817
scroggo@google.com4b90b112012-12-04 15:08:56 +0000818#ifndef SK_DEBUG
819// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
820// should never do this.
821static void test_bad_bitmap() {
822 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
823 // fail.
824 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000825 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000826 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -0700827 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000828 recordingCanvas->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000829 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
scroggo@google.com4b90b112012-12-04 15:08:56 +0000830
831 SkCanvas canvas;
robertphillips9b14f262014-06-04 05:40:44 -0700832 canvas.drawPicture(picture);
scroggo@google.com4b90b112012-12-04 15:08:56 +0000833}
834#endif
835
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000836static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000837 SkPictureRecorder recorder;
mtklein87c41382014-09-08 07:31:18 -0700838 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700839 SkIntToScalar(bitmap.height()));
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000840 canvas->drawBitmap(bitmap, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000841 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
842
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000843 SkDynamicMemoryWStream wStream;
robertphillips3e5c2b12015-03-23 05:46:51 -0700844 sk_tool_utils::PngPixelSerializer serializer;
scroggo895c43b2014-12-11 10:53:58 -0800845 picture->serialize(&wStream, &serializer);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000846 return wStream.copyToData();
847}
848
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000849struct ErrorContext {
850 int fErrors;
851 skiatest::Reporter* fReporter;
852};
853
854static void assert_one_parse_error_cb(SkError error, void* context) {
855 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
856 errorContext->fErrors++;
857 // This test only expects one error, and that is a kParseError. If there are others,
858 // there is some unknown problem.
859 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
860 "This threw more errors than expected.");
861 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
862 SkGetLastErrorString());
863}
864
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000865static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
866 // Create a bitmap that will be encoded.
867 SkBitmap original;
868 make_bm(&original, 100, 100, SK_ColorBLUE, true);
869 SkDynamicMemoryWStream wStream;
870 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
871 return;
872 }
873 SkAutoDataUnref data(wStream.copyToData());
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000874
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000875 SkBitmap bm;
reed5965c8a2015-01-07 18:04:45 -0800876 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm);
reed@google.combf790232013-12-13 19:45:58 +0000877 REPORTER_ASSERT(reporter, installSuccess);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000878
879 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
880 // Flattening original will follow the old path of performing an encode, while flattening bm
881 // will use the already encoded data.
882 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
883 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
884 REPORTER_ASSERT(reporter, picture1->equals(picture2));
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000885 // Now test that a parse error was generated when trying to create a new SkPicture without
886 // providing a function to decode the bitmap.
887 ErrorContext context;
888 context.fErrors = 0;
889 context.fReporter = reporter;
890 SkSetErrorCallback(assert_one_parse_error_cb, &context);
891 SkMemoryStream pictureStream(picture1);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000892 SkClearLastError();
mtklein08d1fcc2014-11-20 09:18:31 -0800893 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000894 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
scroggo@google.com49ce11b2013-04-25 18:29:32 +0000895 SkClearLastError();
896 SkSetErrorCallback(NULL, NULL);
scroggo@google.com7c9d5392012-12-10 15:40:55 +0000897}
898
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000899static void test_clip_bound_opt(skiatest::Reporter* reporter) {
900 // Test for crbug.com/229011
901 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
902 SkIntToScalar(2), SkIntToScalar(2));
903 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
904 SkIntToScalar(1), SkIntToScalar(1));
905 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
906 SkIntToScalar(1), SkIntToScalar(1));
907
908 SkPath invPath;
909 invPath.addOval(rect1);
910 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
911 SkPath path;
912 path.addOval(rect2);
913 SkPath path2;
914 path2.addOval(rect3);
915 SkIRect clipBounds;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000916 SkPictureRecorder recorder;
reedd9544982014-09-09 18:46:22 -0700917
918 // Testing conservative-raster-clip that is enabled by PictureRecord
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000919 {
robertphillips9f1c2412014-06-09 06:25:34 -0700920 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000921 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
922 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
923 REPORTER_ASSERT(reporter, true == nonEmpty);
924 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
925 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
926 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
927 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
928 }
929 {
robertphillips9f1c2412014-06-09 06:25:34 -0700930 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000931 canvas->clipPath(path, SkRegion::kIntersect_Op);
932 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
933 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
934 REPORTER_ASSERT(reporter, true == nonEmpty);
935 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
936 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
937 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
938 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
939 }
940 {
robertphillips9f1c2412014-06-09 06:25:34 -0700941 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000942 canvas->clipPath(path, SkRegion::kIntersect_Op);
943 canvas->clipPath(invPath, SkRegion::kUnion_Op);
944 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
945 REPORTER_ASSERT(reporter, true == nonEmpty);
946 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
947 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
948 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
949 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
950 }
951 {
robertphillips9f1c2412014-06-09 06:25:34 -0700952 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000953 canvas->clipPath(path, SkRegion::kDifference_Op);
954 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
955 REPORTER_ASSERT(reporter, true == nonEmpty);
956 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
957 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
958 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
959 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
960 }
961 {
robertphillips9f1c2412014-06-09 06:25:34 -0700962 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000963 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
964 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
965 // True clip is actually empty in this case, but the best
966 // determination we can make using only bounds as input is that the
967 // clip is included in the bounds of 'path'.
968 REPORTER_ASSERT(reporter, true == nonEmpty);
969 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
970 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
971 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
972 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
973 }
974 {
robertphillips9f1c2412014-06-09 06:25:34 -0700975 SkCanvas* canvas = recorder.beginRecording(10, 10);
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000976 canvas->clipPath(path, SkRegion::kIntersect_Op);
977 canvas->clipPath(path2, SkRegion::kXOR_Op);
978 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
979 REPORTER_ASSERT(reporter, true == nonEmpty);
980 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
981 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
982 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
983 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
984 }
985}
986
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000987/**
988 * A canvas that records the number of clip commands.
989 */
990class ClipCountingCanvas : public SkCanvas {
991public:
commit-bot@chromium.org6d3eaea2014-05-27 23:41:45 +0000992 ClipCountingCanvas(int width, int height)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000993 : INHERITED(width, height)
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000994 , fClipCount(0){
995 }
996
skia.committer@gmail.com370a8992014-03-01 03:02:09 +0000997 virtual void onClipRect(const SkRect& r,
998 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -0700999 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001000 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001001 this->INHERITED::onClipRect(r, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001002 }
1003
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001004 virtual void onClipRRect(const SkRRect& rrect,
1005 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001006 ClipEdgeStyle edgeStyle)override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001007 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001008 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001009 }
1010
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001011 virtual void onClipPath(const SkPath& path,
1012 SkRegion::Op op,
mtklein36352bf2015-03-25 18:17:31 -07001013 ClipEdgeStyle edgeStyle) override {
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001014 fClipCount += 1;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001015 this->INHERITED::onClipPath(path, op, edgeStyle);
1016 }
1017
mtklein36352bf2015-03-25 18:17:31 -07001018 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001019 fClipCount += 1;
1020 this->INHERITED::onClipRegion(deviceRgn, op);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001021 }
1022
1023 unsigned getClipCount() const { return fClipCount; }
1024
1025private:
1026 unsigned fClipCount;
1027
1028 typedef SkCanvas INHERITED;
1029};
1030
1031static void test_clip_expansion(skiatest::Reporter* reporter) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001032 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001033 SkCanvas* canvas = recorder.beginRecording(10, 10);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001034
1035 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1036 // The following expanding clip should not be skipped.
1037 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1038 // Draw something so the optimizer doesn't just fold the world.
1039 SkPaint p;
1040 p.setColor(SK_ColorBLUE);
1041 canvas->drawPaint(p);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001042 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001043
commit-bot@chromium.orge2543102014-01-31 19:42:58 +00001044 ClipCountingCanvas testCanvas(10, 10);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001045 picture->playback(&testCanvas);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001046
1047 // Both clips should be present on playback.
1048 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1049}
1050
tomhudson@google.com381010e2013-10-24 11:12:47 +00001051static void test_hierarchical(skiatest::Reporter* reporter) {
1052 SkBitmap bm;
1053 make_bm(&bm, 10, 10, SK_ColorRED, true);
1054
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001055 SkPictureRecorder recorder;
tomhudson@google.com381010e2013-10-24 11:12:47 +00001056
robertphillips9f1c2412014-06-09 06:25:34 -07001057 recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001058 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1059 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
tomhudson@google.com381010e2013-10-24 11:12:47 +00001060
robertphillips9f1c2412014-06-09 06:25:34 -07001061 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001062 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1063 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
tomhudson@google.com381010e2013-10-24 11:12:47 +00001064
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001065 {
robertphillips9f1c2412014-06-09 06:25:34 -07001066 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001067 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001068 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1069 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1070 }
1071 {
robertphillips9f1c2412014-06-09 06:25:34 -07001072 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips9b14f262014-06-04 05:40:44 -07001073 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001074 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1075 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1076 }
1077 {
robertphillips9f1c2412014-06-09 06:25:34 -07001078 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001079 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001080 canvas->drawPicture(childPlain);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001081 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1082 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1083 }
1084 {
robertphillips9f1c2412014-06-09 06:25:34 -07001085 SkCanvas* canvas = recorder.beginRecording(10, 10);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001086 canvas->drawBitmap(bm, 0, 0);
robertphillips9b14f262014-06-04 05:40:44 -07001087 canvas->drawPicture(childWithBitmap);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001088 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1089 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1090 }
tomhudson@google.com381010e2013-10-24 11:12:47 +00001091}
1092
robertphillips@google.comd5500882014-04-02 23:51:13 +00001093static void test_gen_id(skiatest::Reporter* reporter) {
1094
Robert Phillipscfaeec42014-07-13 12:00:50 -04001095 SkPictureRecorder recorder;
1096 recorder.beginRecording(0, 0);
1097 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001098
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001099 // Empty pictures should still have a valid ID
Robert Phillipscfaeec42014-07-13 12:00:50 -04001100 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001101
robertphillips9f1c2412014-06-09 06:25:34 -07001102 SkCanvas* canvas = recorder.beginRecording(1, 1);
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001103 canvas->drawARGB(255, 255, 255, 255);
1104 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1105 // picture should have a non-zero id after recording
1106 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001107
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001108 // both pictures should have different ids
Robert Phillipscfaeec42014-07-13 12:00:50 -04001109 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
robertphillips@google.comd5500882014-04-02 23:51:13 +00001110}
1111
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001112DEF_TEST(Picture, reporter) {
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001113#ifdef SK_DEBUG
robertphillipsdb539902014-07-01 08:47:04 -07001114 test_deleting_empty_picture();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001115 test_serializing_empty_picture();
scroggo@google.com4b90b112012-12-04 15:08:56 +00001116#else
1117 test_bad_bitmap();
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001118#endif
commit-bot@chromium.orgea7d08e2014-02-13 16:00:51 +00001119 test_unbalanced_save_restores(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00001120 test_peephole();
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001121#if SK_SUPPORT_GPU
mtklein8e126562014-10-01 09:29:35 -07001122 test_gpu_veto(reporter);
robertphillips@google.comb950c6f2014-04-25 00:02:12 +00001123#endif
mtklein8e126562014-10-01 09:29:35 -07001124 test_has_text(reporter);
1125 test_analysis(reporter);
scroggo@google.com7c9d5392012-12-10 15:40:55 +00001126 test_bitmap_with_encoded_data(reporter);
junov@chromium.orgd575eed2013-05-08 15:39:13 +00001127 test_clip_bound_opt(reporter);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +00001128 test_clip_expansion(reporter);
tomhudson@google.com381010e2013-10-24 11:12:47 +00001129 test_hierarchical(reporter);
robertphillips@google.comd5500882014-04-02 23:51:13 +00001130 test_gen_id(reporter);
robertphillips82365912014-11-12 09:32:34 -08001131 test_savelayer_extraction(reporter);
scroggo@google.comd614c6a2012-09-14 17:26:37 +00001132}
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001133
1134static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1135 const SkPaint paint;
1136 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1137 const SkIRect irect = { 2, 2, 3, 3 };
1138
1139 // Don't care what these record, as long as they're legal.
1140 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1141 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001142 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1143 canvas->drawSprite(bitmap, 1, 1);
1144}
1145
1146static void test_draw_bitmaps(SkCanvas* canvas) {
1147 SkBitmap empty;
1148 draw_bitmaps(empty, canvas);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001149 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001150 draw_bitmaps(empty, canvas);
1151}
1152
1153DEF_TEST(Picture_EmptyBitmap, r) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001154 SkPictureRecorder recorder;
robertphillips9f1c2412014-06-09 06:25:34 -07001155 test_draw_bitmaps(recorder.beginRecording(10, 10));
robertphillips@google.com84b18c72014-04-13 19:09:42 +00001156 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001157}
1158
1159DEF_TEST(Canvas_EmptyBitmap, r) {
1160 SkBitmap dst;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +00001161 dst.allocN32Pixels(10, 10);
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001162 SkCanvas canvas(dst);
1163
1164 test_draw_bitmaps(&canvas);
1165}
dneto3f22e8c2014-07-30 15:42:22 -07001166
1167DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1168 // This test is from crbug.com/344987.
1169 // The commands are:
1170 // saveLayer with paint that modifies alpha
1171 // drawBitmapRectToRect
1172 // drawBitmapRectToRect
1173 // restore
1174 // The bug was that this structure was modified so that:
1175 // - The saveLayer and restore were eliminated
1176 // - The alpha was only applied to the first drawBitmapRectToRect
1177
1178 // This test draws blue and red squares inside a 50% transparent
1179 // layer. Both colours should show up muted.
1180 // When the bug is present, the red square (the second bitmap)
1181 // shows upwith full opacity.
1182
1183 SkBitmap blueBM;
1184 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1185 SkBitmap redBM;
1186 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1187 SkPaint semiTransparent;
1188 semiTransparent.setAlpha(0x80);
1189
1190 SkPictureRecorder recorder;
1191 SkCanvas* canvas = recorder.beginRecording(100, 100);
1192 canvas->drawARGB(0, 0, 0, 0);
1193
1194 canvas->saveLayer(0, &semiTransparent);
1195 canvas->drawBitmap(blueBM, 25, 25);
1196 canvas->drawBitmap(redBM, 50, 50);
1197 canvas->restore();
1198
1199 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1200
1201 // Now replay the picture back on another canvas
1202 // and check a couple of its pixels.
1203 SkBitmap replayBM;
1204 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1205 SkCanvas replayCanvas(replayBM);
robertphillipsc5ba71d2014-09-04 08:42:50 -07001206 picture->playback(&replayCanvas);
dneto3f22e8c2014-07-30 15:42:22 -07001207 replayCanvas.flush();
1208
1209 // With the bug present, at (55, 55) we would get a fully opaque red
1210 // intead of a dark red.
1211 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1212 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1213}
mtklein3e8232b2014-08-18 13:39:11 -07001214
1215struct CountingBBH : public SkBBoxHierarchy {
1216 mutable int searchCalls;
schenney23d85932015-03-06 16:20:28 -08001217 SkRect rootBound;
mtklein3e8232b2014-08-18 13:39:11 -07001218
schenney23d85932015-03-06 16:20:28 -08001219 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
mtklein3e8232b2014-08-18 13:39:11 -07001220
mtklein36352bf2015-03-25 18:17:31 -07001221 void search(const SkRect& query, SkTDArray<unsigned>* results) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001222 this->searchCalls++;
1223 }
1224
mtklein36352bf2015-03-25 18:17:31 -07001225 void insert(const SkRect[], int) override {}
1226 virtual size_t bytesUsed() const override { return 0; }
1227 SkRect getRootBound() const override { return rootBound; }
mtklein3e8232b2014-08-18 13:39:11 -07001228};
1229
1230class SpoonFedBBHFactory : public SkBBHFactory {
1231public:
1232 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
mtklein36352bf2015-03-25 18:17:31 -07001233 SkBBoxHierarchy* operator()(const SkRect&) const override {
mtklein3e8232b2014-08-18 13:39:11 -07001234 return SkRef(fBBH);
1235 }
1236private:
1237 SkBBoxHierarchy* fBBH;
1238};
1239
1240// When the canvas clip covers the full picture, we don't need to call the BBH.
1241DEF_TEST(Picture_SkipBBH, r) {
schenney23d85932015-03-06 16:20:28 -08001242 SkRect bound = SkRect::MakeWH(320, 240);
1243 CountingBBH bbh(bound);
mtklein3e8232b2014-08-18 13:39:11 -07001244 SpoonFedBBHFactory factory(&bbh);
1245
1246 SkPictureRecorder recorder;
mtklein9db912c2015-05-19 11:11:26 -07001247 SkCanvas* c = recorder.beginRecording(bound, &factory);
1248 // Record a few ops so we don't hit a small- or empty- picture optimization.
1249 c->drawRect(bound, SkPaint());
1250 c->drawRect(bound, SkPaint());
mtklein3e8232b2014-08-18 13:39:11 -07001251 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1252
1253 SkCanvas big(640, 480), small(300, 200);
1254
robertphillipsc5ba71d2014-09-04 08:42:50 -07001255 picture->playback(&big);
mtklein3e8232b2014-08-18 13:39:11 -07001256 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1257
robertphillipsc5ba71d2014-09-04 08:42:50 -07001258 picture->playback(&small);
mtklein3e8232b2014-08-18 13:39:11 -07001259 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1260}
mtkleind72094d2014-08-27 12:12:23 -07001261
1262DEF_TEST(Picture_BitmapLeak, r) {
1263 SkBitmap mut, immut;
1264 mut.allocN32Pixels(300, 200);
1265 immut.allocN32Pixels(300, 200);
1266 immut.setImmutable();
1267 SkASSERT(!mut.isImmutable());
1268 SkASSERT(immut.isImmutable());
1269
1270 // No one can hold a ref on our pixels yet.
1271 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1272 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1273
reed1bdfd3f2014-11-24 14:41:51 -08001274 SkAutoTUnref<const SkPicture> pic;
1275 {
1276 // we want the recorder to go out of scope before our subsequent checks, so we
1277 // place it inside local braces.
1278 SkPictureRecorder rec;
1279 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1280 canvas->drawBitmap(mut, 0, 0);
1281 canvas->drawBitmap(immut, 800, 600);
1282 pic.reset(rec.endRecording());
1283 }
mtkleind72094d2014-08-27 12:12:23 -07001284
1285 // The picture shares the immutable pixels but copies the mutable ones.
1286 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1287 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1288
1289 // When the picture goes away, it's just our bitmaps holding the refs.
1290 pic.reset(NULL);
1291 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1292 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1293}
mtkleinfeaadee2015-04-08 11:25:48 -07001294
1295// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
1296DEF_TEST(Picture_getRecordingCanvas, r) {
1297 SkPictureRecorder rec;
1298 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1299 for (int i = 0; i < 3; i++) {
1300 rec.beginRecording(100, 100);
1301 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1302 rec.endRecording()->unref();
1303 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1304 }
1305}
mtklein9db912c2015-05-19 11:11:26 -07001306
1307DEF_TEST(MiniRecorderLeftHanging, r) {
1308 // Any shader or other ref-counted effect will do just fine here.
1309 SkPaint paint;
1310 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1311
1312 SkMiniRecorder rec;
1313 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1314 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1315}