1 /*
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 */
7
8 #include "SkBBoxHierarchy.h"
9 #include "SkBlurImageFilter.h"
10 #include "SkCanvas.h"
11 #include "SkColorMatrixFilter.h"
12 #include "SkColorPriv.h"
13 #include "SkDashPathEffect.h"
14 #include "SkData.h"
15 #include "SkImageGenerator.h"
16 #include "SkError.h"
17 #include "SkImageEncoder.h"
18 #include "SkImageGenerator.h"
19 #include "SkLayerInfo.h"
20 #include "SkMD5.h"
21 #include "SkPaint.h"
22 #include "SkPicture.h"
23 #include "SkPictureRecorder.h"
24 #include "SkPictureUtils.h"
25 #include "SkPixelRef.h"
26 #include "SkPixelSerializer.h"
27 #include "SkMiniRecorder.h"
28 #include "SkRRect.h"
29 #include "SkRandom.h"
30 #include "SkRecord.h"
31 #include "SkShader.h"
32 #include "SkStream.h"
33 #include "sk_tool_utils.h"
34
35 #include "Test.h"
36
37 #include "SkLumaColorFilter.h"
38 #include "SkColorFilterImageFilter.h"
39
make_bm(SkBitmap * bm,int w,int h,SkColor color,bool immutable)40 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
41 bm->allocN32Pixels(w, h);
42 bm->eraseColor(color);
43 if (immutable) {
44 bm->setImmutable();
45 }
46 }
47
48 // For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter * reporter)49 static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
50 // We just need _some_ SkImage
51 const SkPMColor pixel = 0;
52 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
53 SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(info, &pixel, sizeof(pixel)));
54
55 SkPictureRecorder recorder;
56 recorder.beginRecording(100,100)->drawImage(image, 0,0);
57 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
58
59 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
60 }
61
62 /* Hit a few SkPicture::Analysis cases not handled elsewhere. */
test_analysis(skiatest::Reporter * reporter)63 static void test_analysis(skiatest::Reporter* reporter) {
64 SkPictureRecorder recorder;
65
66 SkCanvas* canvas = recorder.beginRecording(100, 100);
67 {
68 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
69 }
70 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
71 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
72
73 canvas = recorder.beginRecording(100, 100);
74 {
75 SkPaint paint;
76 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
77 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
78 SkBitmap bitmap;
79 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
80 bitmap.eraseColor(SK_ColorBLUE);
81 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
82 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
83 SkShader::kClamp_TileMode);
84 paint.setShader(shader)->unref();
85 REPORTER_ASSERT(reporter, shader->isABitmap());
86
87 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
88 }
89 picture.reset(recorder.endRecording());
90 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
91 }
92
93
94 #ifdef SK_DEBUG
95 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
96 // in debug mode, so only run in debug mode.
test_deleting_empty_picture()97 static void test_deleting_empty_picture() {
98 SkPictureRecorder recorder;
99 // Creates an SkPictureRecord
100 recorder.beginRecording(0, 0);
101 // Turns that into an SkPicture
102 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
103 // Ceates a new SkPictureRecord
104 recorder.beginRecording(0, 0);
105 }
106
107 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
test_serializing_empty_picture()108 static void test_serializing_empty_picture() {
109 SkPictureRecorder recorder;
110 recorder.beginRecording(0, 0);
111 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
112 SkDynamicMemoryWStream stream;
113 picture->serialize(&stream);
114 }
115 #endif
116
rand_op(SkCanvas * canvas,SkRandom & rand)117 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
118 SkPaint paint;
119 SkRect rect = SkRect::MakeWH(50, 50);
120
121 SkScalar unit = rand.nextUScalar1();
122 if (unit <= 0.3) {
123 // SkDebugf("save\n");
124 canvas->save();
125 } else if (unit <= 0.6) {
126 // SkDebugf("restore\n");
127 canvas->restore();
128 } else if (unit <= 0.9) {
129 // SkDebugf("clip\n");
130 canvas->clipRect(rect);
131 } else {
132 // SkDebugf("draw\n");
133 canvas->drawPaint(paint);
134 }
135 }
136
137 #if SK_SUPPORT_GPU
138
test_gpu_veto(skiatest::Reporter * reporter)139 static void test_gpu_veto(skiatest::Reporter* reporter) {
140 SkPictureRecorder recorder;
141
142 SkCanvas* canvas = recorder.beginRecording(100, 100);
143 {
144 SkPath path;
145 path.moveTo(0, 0);
146 path.lineTo(50, 50);
147
148 SkScalar intervals[] = { 1.0f, 1.0f };
149 SkAutoTUnref<SkPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
150
151 SkPaint paint;
152 paint.setStyle(SkPaint::kStroke_Style);
153 paint.setPathEffect(dash);
154
155 for (int i = 0; i < 50; ++i) {
156 canvas->drawPath(path, paint);
157 }
158 }
159 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
160 // path effects currently render an SkPicture undesireable for GPU rendering
161
162 const char *reason = nullptr;
163 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr, &reason));
164 REPORTER_ASSERT(reporter, reason);
165
166 canvas = recorder.beginRecording(100, 100);
167 {
168 SkPath path;
169
170 path.moveTo(0, 0);
171 path.lineTo(0, 50);
172 path.lineTo(25, 25);
173 path.lineTo(50, 50);
174 path.lineTo(50, 0);
175 path.close();
176 REPORTER_ASSERT(reporter, !path.isConvex());
177
178 SkPaint paint;
179 paint.setAntiAlias(true);
180 for (int i = 0; i < 50; ++i) {
181 canvas->drawPath(path, paint);
182 }
183 }
184 picture.reset(recorder.endRecording());
185 // A lot of small AA concave paths should be fine for GPU rendering
186 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
187
188 canvas = recorder.beginRecording(100, 100);
189 {
190 SkPath path;
191
192 path.moveTo(0, 0);
193 path.lineTo(0, 100);
194 path.lineTo(50, 50);
195 path.lineTo(100, 100);
196 path.lineTo(100, 0);
197 path.close();
198 REPORTER_ASSERT(reporter, !path.isConvex());
199
200 SkPaint paint;
201 paint.setAntiAlias(true);
202 for (int i = 0; i < 50; ++i) {
203 canvas->drawPath(path, paint);
204 }
205 }
206 picture.reset(recorder.endRecording());
207 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
208 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
209
210 canvas = recorder.beginRecording(100, 100);
211 {
212 SkPath path;
213
214 path.moveTo(0, 0);
215 path.lineTo(0, 50);
216 path.lineTo(25, 25);
217 path.lineTo(50, 50);
218 path.lineTo(50, 0);
219 path.close();
220 REPORTER_ASSERT(reporter, !path.isConvex());
221
222 SkPaint paint;
223 paint.setAntiAlias(true);
224 paint.setStyle(SkPaint::kStroke_Style);
225 paint.setStrokeWidth(0);
226 for (int i = 0; i < 50; ++i) {
227 canvas->drawPath(path, paint);
228 }
229 }
230 picture.reset(recorder.endRecording());
231 // hairline stroked AA concave paths are fine for GPU rendering
232 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
233
234 canvas = recorder.beginRecording(100, 100);
235 {
236 SkPaint paint;
237 SkScalar intervals [] = { 10, 20 };
238 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
239 paint.setPathEffect(pe)->unref();
240
241 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
242
243 for (int i = 0; i < 50; ++i) {
244 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
245 }
246 }
247 picture.reset(recorder.endRecording());
248 // fast-path dashed effects are fine for GPU rendering ...
249 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(nullptr));
250
251 canvas = recorder.beginRecording(100, 100);
252 {
253 SkPaint paint;
254 SkScalar intervals [] = { 10, 20 };
255 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
256 paint.setPathEffect(pe)->unref();
257
258 for (int i = 0; i < 50; ++i) {
259 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
260 }
261 }
262 picture.reset(recorder.endRecording());
263 // ... but only when applied to drawPoint() calls
264 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
265
266 // Nest the previous picture inside a new one.
267 canvas = recorder.beginRecording(100, 100);
268 {
269 canvas->drawPicture(picture.get());
270 }
271 picture.reset(recorder.endRecording());
272 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(nullptr));
273 }
274
275 #endif
276
test_savelayer_extraction(skiatest::Reporter * reporter)277 static void test_savelayer_extraction(skiatest::Reporter* reporter) {
278 static const int kWidth = 100;
279 static const int kHeight = 100;
280
281 // Create complex paint that the bounding box computation code can't
282 // optimize away
283 SkScalar blueToRedMatrix[20] = { 0 };
284 blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
285 SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
286 SkAutoTUnref<SkImageFilter> filter(SkColorFilterImageFilter::Create(blueToRed.get()));
287
288 SkPaint complexPaint;
289 complexPaint.setImageFilter(filter);
290
291 SkAutoTUnref<SkPicture> pict, child;
292 SkRTreeFactory bbhFactory;
293
294 {
295 SkPictureRecorder recorder;
296
297 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight),
298 &bbhFactory,
299 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
300
301 c->saveLayer(nullptr, &complexPaint);
302 c->restore();
303
304 child.reset(recorder.endRecording());
305 }
306
307 // create a picture with the structure:
308 // 1)
309 // SaveLayer
310 // Restore
311 // 2)
312 // SaveLayer
313 // Translate
314 // SaveLayer w/ bound
315 // Restore
316 // Restore
317 // 3)
318 // SaveLayer w/ copyable paint
319 // Restore
320 // 4)
321 // SaveLayer
322 // DrawPicture (which has a SaveLayer/Restore pair)
323 // Restore
324 // 5)
325 // SaveLayer
326 // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
327 // Restore
328 {
329 SkPictureRecorder recorder;
330
331 SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
332 SkIntToScalar(kHeight),
333 &bbhFactory,
334 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
335 // 1)
336 c->saveLayer(nullptr, &complexPaint); // layer #0
337 c->restore();
338
339 // 2)
340 c->saveLayer(nullptr, nullptr); // layer #1
341 c->translate(kWidth / 2.0f, kHeight / 2.0f);
342 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
343 c->saveLayer(&r, &complexPaint); // layer #2
344 c->restore();
345 c->restore();
346
347 // 3)
348 {
349 c->saveLayer(nullptr, &complexPaint); // layer #3
350 c->restore();
351 }
352
353 SkPaint layerPaint;
354 layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper
355 // 4)
356 {
357 c->saveLayer(nullptr, &layerPaint); // layer #4
358 c->drawPicture(child); // layer #5 inside picture
359 c->restore();
360 }
361 // 5
362 {
363 SkPaint picturePaint;
364 SkMatrix trans;
365 trans.setTranslate(10, 10);
366
367 c->saveLayer(nullptr, &layerPaint); // layer #6
368 c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
369 c->restore();
370 }
371
372 pict.reset(recorder.endRecording());
373 }
374
375 // Now test out the SaveLayer extraction
376 if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
377 const SkBigPicture* bp = pict->asSkBigPicture();
378 REPORTER_ASSERT(reporter, bp);
379
380 const SkBigPicture::AccelData* data = bp->accelData();
381 REPORTER_ASSERT(reporter, data);
382
383 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
384 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks());
385
386 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0);
387 // The parent/child layers appear in reverse order
388 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2);
389 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1);
390
391 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3);
392
393 // The parent/child layers appear in reverse order
394 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5);
395 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4);
396
397 // The parent/child layers appear in reverse order
398 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7);
399 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6);
400
401 REPORTER_ASSERT(reporter, nullptr == info0.fPicture);
402 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() &&
403 kHeight == info0.fBounds.height());
404 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity());
405 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity());
406 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop);
407 REPORTER_ASSERT(reporter, nullptr != info0.fPaint);
408 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
409
410 REPORTER_ASSERT(reporter, nullptr == info1.fPicture);
411 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() &&
412 kHeight/2.0 == info1.fBounds.height());
413 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity());
414 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity());
415 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft &&
416 kHeight/2.0 == info1.fBounds.fTop);
417 REPORTER_ASSERT(reporter, nullptr == info1.fPaint);
418 REPORTER_ASSERT(reporter, !info1.fIsNested &&
419 info1.fHasNestedLayers); // has a nested SL
420
421 REPORTER_ASSERT(reporter, nullptr == info2.fPicture);
422 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() &&
423 kHeight / 2 == info2.fBounds.height()); // bound reduces size
424 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity());
425 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity());
426 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated
427 kHeight / 2 == info2.fBounds.fTop);
428 REPORTER_ASSERT(reporter, nullptr != info2.fPaint);
429 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
430
431 REPORTER_ASSERT(reporter, nullptr == info3.fPicture);
432 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() &&
433 kHeight == info3.fBounds.height());
434 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity());
435 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity());
436 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop);
437 REPORTER_ASSERT(reporter, info3.fPaint);
438 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
439
440 REPORTER_ASSERT(reporter, nullptr == info4.fPicture);
441 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() &&
442 kHeight == info4.fBounds.height());
443 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop);
444 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity());
445 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity());
446 REPORTER_ASSERT(reporter, info4.fPaint);
447 REPORTER_ASSERT(reporter, !info4.fIsNested &&
448 info4.fHasNestedLayers); // has a nested SL
449
450 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
451 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() &&
452 kHeight == info5.fBounds.height());
453 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop);
454 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity());
455 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity());
456 REPORTER_ASSERT(reporter, nullptr != info5.fPaint);
457 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
458
459 REPORTER_ASSERT(reporter, nullptr == info6.fPicture);
460 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() &&
461 kHeight-10 == info6.fBounds.height());
462 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop);
463 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity());
464 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity());
465 REPORTER_ASSERT(reporter, info6.fPaint);
466 REPORTER_ASSERT(reporter, !info6.fIsNested &&
467 info6.fHasNestedLayers); // has a nested SL
468
469 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
470 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() &&
471 kHeight == info7.fBounds.height());
472 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop);
473 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity());
474 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity());
475 REPORTER_ASSERT(reporter, nullptr != info7.fPaint);
476 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
477 }
478 }
479
test_has_text(skiatest::Reporter * reporter)480 static void test_has_text(skiatest::Reporter* reporter) {
481 SkPictureRecorder recorder;
482
483 SkCanvas* canvas = recorder.beginRecording(100,100);
484 {
485 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
486 }
487 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
488 REPORTER_ASSERT(reporter, !picture->hasText());
489
490 SkPoint point = SkPoint::Make(10, 10);
491 canvas = recorder.beginRecording(100,100);
492 {
493 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
494 }
495 picture.reset(recorder.endRecording());
496 REPORTER_ASSERT(reporter, picture->hasText());
497
498 canvas = recorder.beginRecording(100,100);
499 {
500 canvas->drawPosText("Q", 1, &point, SkPaint());
501 }
502 picture.reset(recorder.endRecording());
503 REPORTER_ASSERT(reporter, picture->hasText());
504
505 canvas = recorder.beginRecording(100,100);
506 {
507 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
508 }
509 picture.reset(recorder.endRecording());
510 REPORTER_ASSERT(reporter, picture->hasText());
511
512 canvas = recorder.beginRecording(100,100);
513 {
514 SkPath path;
515 path.moveTo(0, 0);
516 path.lineTo(50, 50);
517
518 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
519 }
520 picture.reset(recorder.endRecording());
521 REPORTER_ASSERT(reporter, picture->hasText());
522
523 canvas = recorder.beginRecording(100,100);
524 {
525 SkPath path;
526 path.moveTo(0, 0);
527 path.lineTo(50, 50);
528
529 canvas->drawTextOnPath("Q", 1, path, nullptr, SkPaint());
530 }
531 picture.reset(recorder.endRecording());
532 REPORTER_ASSERT(reporter, picture->hasText());
533
534 // Nest the previous picture inside a new one.
535 canvas = recorder.beginRecording(100,100);
536 {
537 canvas->drawPicture(picture.get());
538 }
539 picture.reset(recorder.endRecording());
540 REPORTER_ASSERT(reporter, picture->hasText());
541 }
542
set_canvas_to_save_count_4(SkCanvas * canvas)543 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
544 canvas->restoreToCount(1);
545 canvas->save();
546 canvas->save();
547 canvas->save();
548 }
549
550 /**
551 * A canvas that records the number of saves, saveLayers and restores.
552 */
553 class SaveCountingCanvas : public SkCanvas {
554 public:
SaveCountingCanvas(int width,int height)555 SaveCountingCanvas(int width, int height)
556 : INHERITED(width, height)
557 , fSaveCount(0)
558 , fSaveLayerCount(0)
559 , fRestoreCount(0){
560 }
561
getSaveLayerStrategy(const SaveLayerRec & rec)562 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
563 ++fSaveLayerCount;
564 return this->INHERITED::getSaveLayerStrategy(rec);
565 }
566
willSave()567 void willSave() override {
568 ++fSaveCount;
569 this->INHERITED::willSave();
570 }
571
willRestore()572 void willRestore() override {
573 ++fRestoreCount;
574 this->INHERITED::willRestore();
575 }
576
getSaveCount() const577 unsigned int getSaveCount() const { return fSaveCount; }
getSaveLayerCount() const578 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
getRestoreCount() const579 unsigned int getRestoreCount() const { return fRestoreCount; }
580
581 private:
582 unsigned int fSaveCount;
583 unsigned int fSaveLayerCount;
584 unsigned int fRestoreCount;
585
586 typedef SkCanvas INHERITED;
587 };
588
check_save_state(skiatest::Reporter * reporter,SkPicture * picture,unsigned int numSaves,unsigned int numSaveLayers,unsigned int numRestores)589 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
590 unsigned int numSaves, unsigned int numSaveLayers,
591 unsigned int numRestores) {
592 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
593 SkScalarCeilToInt(picture->cullRect().height()));
594
595 picture->playback(&canvas);
596
597 // Optimizations may have removed these,
598 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
599 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
600 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
601 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
602 }
603
604 // This class exists so SkPicture can friend it and give it access to
605 // the 'partialReplay' method.
606 class SkPictureRecorderReplayTester {
607 public:
Copy(SkPictureRecorder * recorder)608 static SkPicture* Copy(SkPictureRecorder* recorder) {
609 SkPictureRecorder recorder2;
610
611 SkCanvas* canvas = recorder2.beginRecording(10, 10);
612
613 recorder->partialReplay(canvas);
614
615 return recorder2.endRecording();
616 }
617 };
618
create_imbalance(SkCanvas * canvas)619 static void create_imbalance(SkCanvas* canvas) {
620 SkRect clipRect = SkRect::MakeWH(2, 2);
621 SkRect drawRect = SkRect::MakeWH(10, 10);
622 canvas->save();
623 canvas->clipRect(clipRect, SkRegion::kReplace_Op);
624 canvas->translate(1.0f, 1.0f);
625 SkPaint p;
626 p.setColor(SK_ColorGREEN);
627 canvas->drawRect(drawRect, p);
628 // no restore
629 }
630
631 // This tests that replaying a potentially unbalanced picture into a canvas
632 // doesn't affect the canvas' save count or matrix/clip state.
check_balance(skiatest::Reporter * reporter,SkPicture * picture)633 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
634 SkBitmap bm;
635 bm.allocN32Pixels(4, 3);
636 SkCanvas canvas(bm);
637
638 int beforeSaveCount = canvas.getSaveCount();
639
640 SkMatrix beforeMatrix = canvas.getTotalMatrix();
641
642 SkRect beforeClip;
643
644 canvas.getClipBounds(&beforeClip);
645
646 canvas.drawPicture(picture);
647
648 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
649 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
650
651 SkRect afterClip;
652
653 canvas.getClipBounds(&afterClip);
654
655 REPORTER_ASSERT(reporter, afterClip == beforeClip);
656 }
657
658 // Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay,reporter)659 DEF_TEST(PictureRecorder_replay, reporter) {
660 // check save/saveLayer state
661 {
662 SkPictureRecorder recorder;
663
664 SkCanvas* canvas = recorder.beginRecording(10, 10);
665
666 canvas->saveLayer(nullptr, nullptr);
667
668 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
669
670 // The extra save and restore comes from the Copy process.
671 check_save_state(reporter, copy, 2, 1, 3);
672
673 canvas->saveLayer(nullptr, nullptr);
674
675 SkAutoTUnref<SkPicture> final(recorder.endRecording());
676
677 check_save_state(reporter, final, 1, 2, 3);
678
679 // The copy shouldn't pick up any operations added after it was made
680 check_save_state(reporter, copy, 2, 1, 3);
681 }
682
683 // (partially) check leakage of draw ops
684 {
685 SkPictureRecorder recorder;
686
687 SkCanvas* canvas = recorder.beginRecording(10, 10);
688
689 SkRect r = SkRect::MakeWH(5, 5);
690 SkPaint p;
691
692 canvas->drawRect(r, p);
693
694 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
695
696 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
697
698 SkBitmap bm;
699 make_bm(&bm, 10, 10, SK_ColorRED, true);
700
701 r.offset(5.0f, 5.0f);
702 canvas->drawBitmapRect(bm, r, nullptr);
703
704 SkAutoTUnref<SkPicture> final(recorder.endRecording());
705 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
706
707 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
708
709 // The snapshot shouldn't pick up any operations added after it was made
710 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
711 }
712
713 // Recreate the Android partialReplay test case
714 {
715 SkPictureRecorder recorder;
716
717 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
718 create_imbalance(canvas);
719
720 int expectedSaveCount = canvas->getSaveCount();
721
722 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
723 check_balance(reporter, copy);
724
725 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
726
727 // End the recording of source to test the picture finalization
728 // process isn't complicated by the partialReplay step
729 SkAutoTUnref<SkPicture> final(recorder.endRecording());
730 }
731 }
732
test_unbalanced_save_restores(skiatest::Reporter * reporter)733 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
734 SkCanvas testCanvas(100, 100);
735 set_canvas_to_save_count_4(&testCanvas);
736
737 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
738
739 SkPaint paint;
740 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
741
742 SkPictureRecorder recorder;
743
744 {
745 // Create picture with 2 unbalanced saves
746 SkCanvas* canvas = recorder.beginRecording(100, 100);
747 canvas->save();
748 canvas->translate(10, 10);
749 canvas->drawRect(rect, paint);
750 canvas->save();
751 canvas->translate(10, 10);
752 canvas->drawRect(rect, paint);
753 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
754
755 testCanvas.drawPicture(extraSavePicture);
756 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
757 }
758
759 set_canvas_to_save_count_4(&testCanvas);
760
761 {
762 // Create picture with 2 unbalanced restores
763 SkCanvas* canvas = recorder.beginRecording(100, 100);
764 canvas->save();
765 canvas->translate(10, 10);
766 canvas->drawRect(rect, paint);
767 canvas->save();
768 canvas->translate(10, 10);
769 canvas->drawRect(rect, paint);
770 canvas->restore();
771 canvas->restore();
772 canvas->restore();
773 canvas->restore();
774 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
775
776 testCanvas.drawPicture(extraRestorePicture);
777 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
778 }
779
780 set_canvas_to_save_count_4(&testCanvas);
781
782 {
783 SkCanvas* canvas = recorder.beginRecording(100, 100);
784 canvas->translate(10, 10);
785 canvas->drawRect(rect, paint);
786 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
787
788 testCanvas.drawPicture(noSavePicture);
789 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
790 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
791 }
792 }
793
test_peephole()794 static void test_peephole() {
795 SkRandom rand;
796
797 SkPictureRecorder recorder;
798
799 for (int j = 0; j < 100; j++) {
800 SkRandom rand2(rand); // remember the seed
801
802 SkCanvas* canvas = recorder.beginRecording(100, 100);
803
804 for (int i = 0; i < 1000; ++i) {
805 rand_op(canvas, rand);
806 }
807 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
808
809 rand = rand2;
810 }
811
812 {
813 SkCanvas* canvas = recorder.beginRecording(100, 100);
814 SkRect rect = SkRect::MakeWH(50, 50);
815
816 for (int i = 0; i < 100; ++i) {
817 canvas->save();
818 }
819 while (canvas->getSaveCount() > 1) {
820 canvas->clipRect(rect);
821 canvas->restore();
822 }
823 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
824 }
825 }
826
827 #ifndef SK_DEBUG
828 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
829 // should never do this.
test_bad_bitmap()830 static void test_bad_bitmap() {
831 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
832 // fail.
833 SkBitmap bm;
834 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
835 SkPictureRecorder recorder;
836 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
837 recordingCanvas->drawBitmap(bm, 0, 0);
838 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
839
840 SkCanvas canvas;
841 canvas.drawPicture(picture);
842 }
843 #endif
844
serialized_picture_from_bitmap(const SkBitmap & bitmap)845 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
846 SkPictureRecorder recorder;
847 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
848 SkIntToScalar(bitmap.height()));
849 canvas->drawBitmap(bitmap, 0, 0);
850 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
851
852 SkDynamicMemoryWStream wStream;
853 SkAutoTUnref<SkPixelSerializer> serializer(
854 SkImageEncoder::CreatePixelSerializer());
855 picture->serialize(&wStream, serializer);
856 return wStream.copyToData();
857 }
858
859 struct ErrorContext {
860 int fErrors;
861 skiatest::Reporter* fReporter;
862 };
863
assert_one_parse_error_cb(SkError error,void * context)864 static void assert_one_parse_error_cb(SkError error, void* context) {
865 ErrorContext* errorContext = static_cast<ErrorContext*>(context);
866 errorContext->fErrors++;
867 // This test only expects one error, and that is a kParseError. If there are others,
868 // there is some unknown problem.
869 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
870 "This threw more errors than expected.");
871 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
872 SkGetLastErrorString());
873 }
874
md5(const SkBitmap & bm,SkMD5::Digest * digest)875 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
876 SkAutoLockPixels autoLockPixels(bm);
877 SkASSERT(bm.getPixels());
878 SkMD5 md5;
879 size_t rowLen = bm.info().bytesPerPixel() * bm.width();
880 for (int y = 0; y < bm.height(); ++y) {
881 md5.update(static_cast<uint8_t*>(bm.getAddr(0, y)), rowLen);
882 }
883 md5.finish(*digest);
884 }
885
DEF_TEST(Picture_EncodedData,reporter)886 DEF_TEST(Picture_EncodedData, reporter) {
887 // Create a bitmap that will be encoded.
888 SkBitmap original;
889 make_bm(&original, 100, 100, SK_ColorBLUE, true);
890 SkDynamicMemoryWStream wStream;
891 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
892 return;
893 }
894 SkAutoDataUnref data(wStream.copyToData());
895
896 SkBitmap bm;
897 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &bm);
898 REPORTER_ASSERT(reporter, installSuccess);
899
900 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
901 // Flattening original will follow the old path of performing an encode, while flattening bm
902 // will use the already encoded data.
903 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
904 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
905 REPORTER_ASSERT(reporter, picture1->equals(picture2));
906
907 // Now test that a parse error was generated when trying to create a new SkPicture without
908 // providing a function to decode the bitmap.
909 ErrorContext context;
910 context.fErrors = 0;
911 context.fReporter = reporter;
912 SkSetErrorCallback(assert_one_parse_error_cb, &context);
913 SkMemoryStream pictureStream(picture1);
914 SkClearLastError();
915 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, nullptr));
916 REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr);
917 SkClearLastError();
918 SkSetErrorCallback(nullptr, nullptr);
919
920 // Test that using the version of CreateFromStream that just takes a stream also decodes the
921 // bitmap. Drawing this picture should look exactly like the original bitmap.
922 SkMD5::Digest referenceDigest;
923 md5(original, &referenceDigest);
924
925 SkBitmap dst;
926 dst.allocPixels(original.info());
927 dst.eraseColor(SK_ColorRED);
928 SkCanvas canvas(dst);
929
930 pictureStream.rewind();
931 pictureFromStream.reset(SkPicture::CreateFromStream(&pictureStream));
932 canvas.drawPicture(pictureFromStream.get());
933
934 SkMD5::Digest digest2;
935 md5(dst, &digest2);
936 REPORTER_ASSERT(reporter, referenceDigest == digest2);
937 }
938
test_clip_bound_opt(skiatest::Reporter * reporter)939 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
940 // Test for crbug.com/229011
941 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
942 SkIntToScalar(2), SkIntToScalar(2));
943 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
944 SkIntToScalar(1), SkIntToScalar(1));
945 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
946 SkIntToScalar(1), SkIntToScalar(1));
947
948 SkPath invPath;
949 invPath.addOval(rect1);
950 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
951 SkPath path;
952 path.addOval(rect2);
953 SkPath path2;
954 path2.addOval(rect3);
955 SkIRect clipBounds;
956 SkPictureRecorder recorder;
957
958 // Testing conservative-raster-clip that is enabled by PictureRecord
959 {
960 SkCanvas* canvas = recorder.beginRecording(10, 10);
961 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
962 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
963 REPORTER_ASSERT(reporter, true == nonEmpty);
964 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
965 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
966 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
967 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
968 }
969 {
970 SkCanvas* canvas = recorder.beginRecording(10, 10);
971 canvas->clipPath(path, SkRegion::kIntersect_Op);
972 canvas->clipPath(invPath, SkRegion::kIntersect_Op);
973 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
974 REPORTER_ASSERT(reporter, true == nonEmpty);
975 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
976 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
977 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
978 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
979 }
980 {
981 SkCanvas* canvas = recorder.beginRecording(10, 10);
982 canvas->clipPath(path, SkRegion::kIntersect_Op);
983 canvas->clipPath(invPath, SkRegion::kUnion_Op);
984 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
985 REPORTER_ASSERT(reporter, true == nonEmpty);
986 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
987 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
988 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
989 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
990 }
991 {
992 SkCanvas* canvas = recorder.beginRecording(10, 10);
993 canvas->clipPath(path, SkRegion::kDifference_Op);
994 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
995 REPORTER_ASSERT(reporter, true == nonEmpty);
996 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
997 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
998 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
999 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
1000 }
1001 {
1002 SkCanvas* canvas = recorder.beginRecording(10, 10);
1003 canvas->clipPath(path, SkRegion::kReverseDifference_Op);
1004 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
1005 // True clip is actually empty in this case, but the best
1006 // determination we can make using only bounds as input is that the
1007 // clip is included in the bounds of 'path'.
1008 REPORTER_ASSERT(reporter, true == nonEmpty);
1009 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
1010 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
1011 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
1012 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
1013 }
1014 {
1015 SkCanvas* canvas = recorder.beginRecording(10, 10);
1016 canvas->clipPath(path, SkRegion::kIntersect_Op);
1017 canvas->clipPath(path2, SkRegion::kXOR_Op);
1018 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
1019 REPORTER_ASSERT(reporter, true == nonEmpty);
1020 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
1021 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
1022 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
1023 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
1024 }
1025 }
1026
test_cull_rect_reset(skiatest::Reporter * reporter)1027 static void test_cull_rect_reset(skiatest::Reporter* reporter) {
1028 SkPictureRecorder recorder;
1029 SkRect bounds = SkRect::MakeWH(10, 10);
1030 SkRTreeFactory factory;
1031 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
1032 bounds = SkRect::MakeWH(100, 100);
1033 SkPaint paint;
1034 canvas->drawRect(bounds, paint);
1035 canvas->drawRect(bounds, paint);
1036 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds));
1037 const SkBigPicture* picture = p->asSkBigPicture();
1038 REPORTER_ASSERT(reporter, picture);
1039
1040 SkRect finalCullRect = picture->cullRect();
1041 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
1042 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
1043 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
1044 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
1045
1046 const SkBBoxHierarchy* pictureBBH = picture->bbh();
1047 SkRect bbhCullRect = pictureBBH->getRootBound();
1048 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
1049 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
1050 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
1051 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
1052 }
1053
1054
1055 /**
1056 * A canvas that records the number of clip commands.
1057 */
1058 class ClipCountingCanvas : public SkCanvas {
1059 public:
ClipCountingCanvas(int width,int height)1060 ClipCountingCanvas(int width, int height)
1061 : INHERITED(width, height)
1062 , fClipCount(0){
1063 }
1064
onClipRect(const SkRect & r,SkRegion::Op op,ClipEdgeStyle edgeStyle)1065 virtual void onClipRect(const SkRect& r,
1066 SkRegion::Op op,
1067 ClipEdgeStyle edgeStyle) override {
1068 fClipCount += 1;
1069 this->INHERITED::onClipRect(r, op, edgeStyle);
1070 }
1071
onClipRRect(const SkRRect & rrect,SkRegion::Op op,ClipEdgeStyle edgeStyle)1072 virtual void onClipRRect(const SkRRect& rrect,
1073 SkRegion::Op op,
1074 ClipEdgeStyle edgeStyle)override {
1075 fClipCount += 1;
1076 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
1077 }
1078
onClipPath(const SkPath & path,SkRegion::Op op,ClipEdgeStyle edgeStyle)1079 virtual void onClipPath(const SkPath& path,
1080 SkRegion::Op op,
1081 ClipEdgeStyle edgeStyle) override {
1082 fClipCount += 1;
1083 this->INHERITED::onClipPath(path, op, edgeStyle);
1084 }
1085
onClipRegion(const SkRegion & deviceRgn,SkRegion::Op op)1086 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
1087 fClipCount += 1;
1088 this->INHERITED::onClipRegion(deviceRgn, op);
1089 }
1090
getClipCount() const1091 unsigned getClipCount() const { return fClipCount; }
1092
1093 private:
1094 unsigned fClipCount;
1095
1096 typedef SkCanvas INHERITED;
1097 };
1098
test_clip_expansion(skiatest::Reporter * reporter)1099 static void test_clip_expansion(skiatest::Reporter* reporter) {
1100 SkPictureRecorder recorder;
1101 SkCanvas* canvas = recorder.beginRecording(10, 10);
1102
1103 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
1104 // The following expanding clip should not be skipped.
1105 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
1106 // Draw something so the optimizer doesn't just fold the world.
1107 SkPaint p;
1108 p.setColor(SK_ColorBLUE);
1109 canvas->drawPaint(p);
1110 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1111
1112 ClipCountingCanvas testCanvas(10, 10);
1113 picture->playback(&testCanvas);
1114
1115 // Both clips should be present on playback.
1116 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
1117 }
1118
test_hierarchical(skiatest::Reporter * reporter)1119 static void test_hierarchical(skiatest::Reporter* reporter) {
1120 SkBitmap bm;
1121 make_bm(&bm, 10, 10, SK_ColorRED, true);
1122
1123 SkPictureRecorder recorder;
1124
1125 recorder.beginRecording(10, 10);
1126 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
1127 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
1128
1129 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
1130 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
1131 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
1132
1133 {
1134 SkCanvas* canvas = recorder.beginRecording(10, 10);
1135 canvas->drawPicture(childPlain);
1136 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
1137 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
1138 }
1139 {
1140 SkCanvas* canvas = recorder.beginRecording(10, 10);
1141 canvas->drawPicture(childWithBitmap);
1142 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
1143 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
1144 }
1145 {
1146 SkCanvas* canvas = recorder.beginRecording(10, 10);
1147 canvas->drawBitmap(bm, 0, 0);
1148 canvas->drawPicture(childPlain);
1149 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
1150 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
1151 }
1152 {
1153 SkCanvas* canvas = recorder.beginRecording(10, 10);
1154 canvas->drawBitmap(bm, 0, 0);
1155 canvas->drawPicture(childWithBitmap);
1156 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
1157 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
1158 }
1159 }
1160
test_gen_id(skiatest::Reporter * reporter)1161 static void test_gen_id(skiatest::Reporter* reporter) {
1162
1163 SkPictureRecorder recorder;
1164 recorder.beginRecording(0, 0);
1165 SkAutoTUnref<SkPicture> empty(recorder.endRecording());
1166
1167 // Empty pictures should still have a valid ID
1168 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
1169
1170 SkCanvas* canvas = recorder.beginRecording(1, 1);
1171 canvas->drawARGB(255, 255, 255, 255);
1172 SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
1173 // picture should have a non-zero id after recording
1174 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
1175
1176 // both pictures should have different ids
1177 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
1178 }
1179
test_typeface(skiatest::Reporter * reporter)1180 static void test_typeface(skiatest::Reporter* reporter) {
1181 SkPictureRecorder recorder;
1182 SkCanvas* canvas = recorder.beginRecording(10, 10);
1183 SkPaint paint;
1184 paint.setTypeface(SkTypeface::CreateFromName("Arial", SkTypeface::kItalic));
1185 canvas->drawText("Q", 1, 0, 10, paint);
1186 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1187 REPORTER_ASSERT(reporter, picture->hasText());
1188 SkDynamicMemoryWStream stream;
1189 picture->serialize(&stream);
1190 }
1191
DEF_TEST(Picture,reporter)1192 DEF_TEST(Picture, reporter) {
1193 test_typeface(reporter);
1194 #ifdef SK_DEBUG
1195 test_deleting_empty_picture();
1196 test_serializing_empty_picture();
1197 #else
1198 test_bad_bitmap();
1199 #endif
1200 test_unbalanced_save_restores(reporter);
1201 test_peephole();
1202 #if SK_SUPPORT_GPU
1203 test_gpu_veto(reporter);
1204 #endif
1205 test_has_text(reporter);
1206 test_images_are_found_by_willPlayBackBitmaps(reporter);
1207 test_analysis(reporter);
1208 test_clip_bound_opt(reporter);
1209 test_clip_expansion(reporter);
1210 test_hierarchical(reporter);
1211 test_gen_id(reporter);
1212 test_savelayer_extraction(reporter);
1213 test_cull_rect_reset(reporter);
1214 }
1215
draw_bitmaps(const SkBitmap bitmap,SkCanvas * canvas)1216 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
1217 const SkPaint paint;
1218 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
1219 const SkIRect irect = { 2, 2, 3, 3 };
1220
1221 // Don't care what these record, as long as they're legal.
1222 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
1223 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
1224 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
1225 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
1226 }
1227
test_draw_bitmaps(SkCanvas * canvas)1228 static void test_draw_bitmaps(SkCanvas* canvas) {
1229 SkBitmap empty;
1230 draw_bitmaps(empty, canvas);
1231 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
1232 draw_bitmaps(empty, canvas);
1233 }
1234
DEF_TEST(Picture_EmptyBitmap,r)1235 DEF_TEST(Picture_EmptyBitmap, r) {
1236 SkPictureRecorder recorder;
1237 test_draw_bitmaps(recorder.beginRecording(10, 10));
1238 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1239 }
1240
DEF_TEST(Canvas_EmptyBitmap,r)1241 DEF_TEST(Canvas_EmptyBitmap, r) {
1242 SkBitmap dst;
1243 dst.allocN32Pixels(10, 10);
1244 SkCanvas canvas(dst);
1245
1246 test_draw_bitmaps(&canvas);
1247 }
1248
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore,reporter)1249 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
1250 // This test is from crbug.com/344987.
1251 // The commands are:
1252 // saveLayer with paint that modifies alpha
1253 // drawBitmapRect
1254 // drawBitmapRect
1255 // restore
1256 // The bug was that this structure was modified so that:
1257 // - The saveLayer and restore were eliminated
1258 // - The alpha was only applied to the first drawBitmapRectToRect
1259
1260 // This test draws blue and red squares inside a 50% transparent
1261 // layer. Both colours should show up muted.
1262 // When the bug is present, the red square (the second bitmap)
1263 // shows upwith full opacity.
1264
1265 SkBitmap blueBM;
1266 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
1267 SkBitmap redBM;
1268 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
1269 SkPaint semiTransparent;
1270 semiTransparent.setAlpha(0x80);
1271
1272 SkPictureRecorder recorder;
1273 SkCanvas* canvas = recorder.beginRecording(100, 100);
1274 canvas->drawARGB(0, 0, 0, 0);
1275
1276 canvas->saveLayer(0, &semiTransparent);
1277 canvas->drawBitmap(blueBM, 25, 25);
1278 canvas->drawBitmap(redBM, 50, 50);
1279 canvas->restore();
1280
1281 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1282
1283 // Now replay the picture back on another canvas
1284 // and check a couple of its pixels.
1285 SkBitmap replayBM;
1286 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
1287 SkCanvas replayCanvas(replayBM);
1288 picture->playback(&replayCanvas);
1289 replayCanvas.flush();
1290
1291 // With the bug present, at (55, 55) we would get a fully opaque red
1292 // intead of a dark red.
1293 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
1294 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
1295 }
1296
1297 struct CountingBBH : public SkBBoxHierarchy {
1298 mutable int searchCalls;
1299 SkRect rootBound;
1300
CountingBBHCountingBBH1301 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
1302
searchCountingBBH1303 void search(const SkRect& query, SkTDArray<int>* results) const override {
1304 this->searchCalls++;
1305 }
1306
insertCountingBBH1307 void insert(const SkRect[], int) override {}
bytesUsedCountingBBH1308 virtual size_t bytesUsed() const override { return 0; }
getRootBoundCountingBBH1309 SkRect getRootBound() const override { return rootBound; }
1310 };
1311
1312 class SpoonFedBBHFactory : public SkBBHFactory {
1313 public:
SpoonFedBBHFactory(SkBBoxHierarchy * bbh)1314 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
operator ()(const SkRect &) const1315 SkBBoxHierarchy* operator()(const SkRect&) const override {
1316 return SkRef(fBBH);
1317 }
1318 private:
1319 SkBBoxHierarchy* fBBH;
1320 };
1321
1322 // When the canvas clip covers the full picture, we don't need to call the BBH.
DEF_TEST(Picture_SkipBBH,r)1323 DEF_TEST(Picture_SkipBBH, r) {
1324 SkRect bound = SkRect::MakeWH(320, 240);
1325 CountingBBH bbh(bound);
1326 SpoonFedBBHFactory factory(&bbh);
1327
1328 SkPictureRecorder recorder;
1329 SkCanvas* c = recorder.beginRecording(bound, &factory);
1330 // Record a few ops so we don't hit a small- or empty- picture optimization.
1331 c->drawRect(bound, SkPaint());
1332 c->drawRect(bound, SkPaint());
1333 SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
1334
1335 SkCanvas big(640, 480), small(300, 200);
1336
1337 picture->playback(&big);
1338 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1339
1340 picture->playback(&small);
1341 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1342 }
1343
DEF_TEST(Picture_BitmapLeak,r)1344 DEF_TEST(Picture_BitmapLeak, r) {
1345 SkBitmap mut, immut;
1346 mut.allocN32Pixels(300, 200);
1347 immut.allocN32Pixels(300, 200);
1348 immut.setImmutable();
1349 SkASSERT(!mut.isImmutable());
1350 SkASSERT(immut.isImmutable());
1351
1352 // No one can hold a ref on our pixels yet.
1353 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1354 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1355
1356 SkAutoTUnref<const SkPicture> pic;
1357 {
1358 // we want the recorder to go out of scope before our subsequent checks, so we
1359 // place it inside local braces.
1360 SkPictureRecorder rec;
1361 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1362 canvas->drawBitmap(mut, 0, 0);
1363 canvas->drawBitmap(immut, 800, 600);
1364 pic.reset(rec.endRecording());
1365 }
1366
1367 // The picture shares the immutable pixels but copies the mutable ones.
1368 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1369 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1370
1371 // When the picture goes away, it's just our bitmaps holding the refs.
1372 pic.reset(nullptr);
1373 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1374 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1375 }
1376
1377 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
DEF_TEST(Picture_getRecordingCanvas,r)1378 DEF_TEST(Picture_getRecordingCanvas, r) {
1379 SkPictureRecorder rec;
1380 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1381 for (int i = 0; i < 3; i++) {
1382 rec.beginRecording(100, 100);
1383 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1384 rec.endRecording()->unref();
1385 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1386 }
1387 }
1388
DEF_TEST(MiniRecorderLeftHanging,r)1389 DEF_TEST(MiniRecorderLeftHanging, r) {
1390 // Any shader or other ref-counted effect will do just fine here.
1391 SkPaint paint;
1392 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref();
1393
1394 SkMiniRecorder rec;
1395 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1396 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1397 }
1398
DEF_TEST(Picture_preserveCullRect,r)1399 DEF_TEST(Picture_preserveCullRect, r) {
1400 SkPictureRecorder recorder;
1401
1402 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1403 c->clear(SK_ColorCYAN);
1404
1405 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
1406 SkDynamicMemoryWStream wstream;
1407 picture->serialize(&wstream);
1408
1409 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
1410 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream));
1411
1412 REPORTER_ASSERT(r, SkToBool(deserializedPicture));
1413 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1414 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1415 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1416 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1417 }
1418