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 "SkBigPicture.h"
9 #include "SkBBoxHierarchy.h"
10 #include "SkBlurImageFilter.h"
11 #include "SkCanvas.h"
12 #include "SkColorMatrixFilter.h"
13 #include "SkColorPriv.h"
14 #include "SkDashPathEffect.h"
15 #include "SkData.h"
16 #include "SkImageGenerator.h"
17 #include "SkImageEncoder.h"
18 #include "SkImageGenerator.h"
19 #include "SkMD5.h"
20 #include "SkPaint.h"
21 #include "SkPicture.h"
22 #include "SkPictureAnalyzer.h"
23 #include "SkPictureRecorder.h"
24 #include "SkPixelRef.h"
25 #include "SkPixelSerializer.h"
26 #include "SkMiniRecorder.h"
27 #include "SkRRect.h"
28 #include "SkRandom.h"
29 #include "SkRecord.h"
30 #include "SkShader.h"
31 #include "SkStream.h"
32 #include "sk_tool_utils.h"
33
34 #include "Test.h"
35
36 #include "SkLumaColorFilter.h"
37 #include "SkColorFilterImageFilter.h"
38
make_bm(SkBitmap * bm,int w,int h,SkColor color,bool immutable)39 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
40 bm->allocN32Pixels(w, h);
41 bm->eraseColor(color);
42 if (immutable) {
43 bm->setImmutable();
44 }
45 }
46
47 // For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter * reporter)48 static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
49 // We just need _some_ SkImage
50 const SkPMColor pixel = 0;
51 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
52 sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel))));
53
54 SkPictureRecorder recorder;
55 recorder.beginRecording(100,100)->drawImage(image, 0,0);
56 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
57
58 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
59 }
60
61 /* Hit a few SkPicture::Analysis cases not handled elsewhere. */
test_analysis(skiatest::Reporter * reporter)62 static void test_analysis(skiatest::Reporter* reporter) {
63 SkPictureRecorder recorder;
64
65 SkCanvas* canvas = recorder.beginRecording(100, 100);
66 {
67 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
68 }
69 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
70 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
71
72 canvas = recorder.beginRecording(100, 100);
73 {
74 SkPaint paint;
75 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
76 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
77 SkBitmap bitmap;
78 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
79 bitmap.eraseColor(SK_ColorBLUE);
80 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
81 paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
82 SkShader::kClamp_TileMode));
83 REPORTER_ASSERT(reporter, paint.getShader()->isAImage());
84
85 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
86 }
87 REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps());
88 }
89
90
91 #ifdef SK_DEBUG
92 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
93 // in debug mode, so only run in debug mode.
test_deleting_empty_picture()94 static void test_deleting_empty_picture() {
95 SkPictureRecorder recorder;
96 // Creates an SkPictureRecord
97 recorder.beginRecording(0, 0);
98 // Turns that into an SkPicture
99 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
100 // Ceates a new SkPictureRecord
101 recorder.beginRecording(0, 0);
102 }
103
104 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
test_serializing_empty_picture()105 static void test_serializing_empty_picture() {
106 SkPictureRecorder recorder;
107 recorder.beginRecording(0, 0);
108 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
109 SkDynamicMemoryWStream stream;
110 picture->serialize(&stream);
111 }
112 #endif
113
rand_op(SkCanvas * canvas,SkRandom & rand)114 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
115 SkPaint paint;
116 SkRect rect = SkRect::MakeWH(50, 50);
117
118 SkScalar unit = rand.nextUScalar1();
119 if (unit <= 0.3) {
120 // SkDebugf("save\n");
121 canvas->save();
122 } else if (unit <= 0.6) {
123 // SkDebugf("restore\n");
124 canvas->restore();
125 } else if (unit <= 0.9) {
126 // SkDebugf("clip\n");
127 canvas->clipRect(rect);
128 } else {
129 // SkDebugf("draw\n");
130 canvas->drawPaint(paint);
131 }
132 }
133
134 #if SK_SUPPORT_GPU
135
make_convex_path()136 static SkPath make_convex_path() {
137 SkPath path;
138 path.lineTo(100, 0);
139 path.lineTo(50, 100);
140 path.close();
141
142 return path;
143 }
144
make_concave_path()145 static SkPath make_concave_path() {
146 SkPath path;
147 path.lineTo(50, 50);
148 path.lineTo(100, 0);
149 path.lineTo(50, 100);
150 path.close();
151
152 return path;
153 }
154
test_gpu_veto(skiatest::Reporter * reporter)155 static void test_gpu_veto(skiatest::Reporter* reporter) {
156 SkPictureRecorder recorder;
157
158 SkCanvas* canvas = recorder.beginRecording(100, 100);
159 {
160 SkPath path;
161 path.moveTo(0, 0);
162 path.lineTo(50, 50);
163
164 SkScalar intervals[] = { 1.0f, 1.0f };
165 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
166
167 SkPaint paint;
168 paint.setStyle(SkPaint::kStroke_Style);
169 paint.setPathEffect(dash);
170
171 for (int i = 0; i < 50; ++i) {
172 canvas->drawPath(path, paint);
173 }
174 }
175 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
176 // path effects currently render an SkPicture undesireable for GPU rendering
177
178 const char *reason = nullptr;
179 REPORTER_ASSERT(reporter,
180 !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason));
181 REPORTER_ASSERT(reporter, reason);
182
183 canvas = recorder.beginRecording(100, 100);
184 {
185 SkPath path;
186
187 path.moveTo(0, 0);
188 path.lineTo(0, 50);
189 path.lineTo(25, 25);
190 path.lineTo(50, 50);
191 path.lineTo(50, 0);
192 path.close();
193 REPORTER_ASSERT(reporter, !path.isConvex());
194
195 SkPaint paint;
196 paint.setAntiAlias(true);
197 for (int i = 0; i < 50; ++i) {
198 canvas->drawPath(path, paint);
199 }
200 }
201 picture = recorder.finishRecordingAsPicture();
202 // A lot of small AA concave paths should be fine for GPU rendering
203 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
204
205 canvas = recorder.beginRecording(100, 100);
206 {
207 SkPath path;
208
209 path.moveTo(0, 0);
210 path.lineTo(0, 100);
211 path.lineTo(50, 50);
212 path.lineTo(100, 100);
213 path.lineTo(100, 0);
214 path.close();
215 REPORTER_ASSERT(reporter, !path.isConvex());
216
217 SkPaint paint;
218 paint.setAntiAlias(true);
219 for (int i = 0; i < 50; ++i) {
220 canvas->drawPath(path, paint);
221 }
222 }
223 picture = recorder.finishRecordingAsPicture();
224 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
225 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
226
227 canvas = recorder.beginRecording(100, 100);
228 {
229 SkPath path;
230
231 path.moveTo(0, 0);
232 path.lineTo(0, 50);
233 path.lineTo(25, 25);
234 path.lineTo(50, 50);
235 path.lineTo(50, 0);
236 path.close();
237 REPORTER_ASSERT(reporter, !path.isConvex());
238
239 SkPaint paint;
240 paint.setAntiAlias(true);
241 paint.setStyle(SkPaint::kStroke_Style);
242 paint.setStrokeWidth(0);
243 for (int i = 0; i < 50; ++i) {
244 canvas->drawPath(path, paint);
245 }
246 }
247 picture = recorder.finishRecordingAsPicture();
248 // hairline stroked AA concave paths are fine for GPU rendering
249 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
250
251 canvas = recorder.beginRecording(100, 100);
252 {
253 SkPaint paint;
254 SkScalar intervals [] = { 10, 20 };
255 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
256
257 SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
258
259 for (int i = 0; i < 50; ++i) {
260 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
261 }
262 }
263 picture = recorder.finishRecordingAsPicture();
264 // fast-path dashed effects are fine for GPU rendering ...
265 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
266
267 canvas = recorder.beginRecording(100, 100);
268 {
269 SkPaint paint;
270 SkScalar intervals [] = { 10, 20 };
271 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
272
273 for (int i = 0; i < 50; ++i) {
274 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
275 }
276 }
277 picture = recorder.finishRecordingAsPicture();
278 // ... but only when applied to drawPoint() calls
279 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
280
281 canvas = recorder.beginRecording(100, 100);
282 {
283 const SkPath convexClip = make_convex_path();
284 const SkPath concaveClip = make_concave_path();
285
286 for (int i = 0; i < 50; ++i) {
287 canvas->clipPath(convexClip);
288 canvas->clipPath(concaveClip);
289 canvas->clipPath(convexClip, kIntersect_SkClipOp, true);
290 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
291 }
292 }
293 picture = recorder.finishRecordingAsPicture();
294 // Convex clips and non-AA concave clips are fine on the GPU.
295 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
296
297 canvas = recorder.beginRecording(100, 100);
298 {
299 const SkPath concaveClip = make_concave_path();
300 for (int i = 0; i < 50; ++i) {
301 canvas->clipPath(concaveClip, kIntersect_SkClipOp, true);
302 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
303 }
304 }
305 picture = recorder.finishRecordingAsPicture();
306 // ... but AA concave clips are not.
307 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
308
309 // Nest the previous picture inside a new one.
310 canvas = recorder.beginRecording(100, 100);
311 {
312 canvas->drawPicture(picture);
313 }
314 picture = recorder.finishRecordingAsPicture();
315 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
316 }
317
318 #endif // SK_SUPPORT_GPU
319
set_canvas_to_save_count_4(SkCanvas * canvas)320 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
321 canvas->restoreToCount(1);
322 canvas->save();
323 canvas->save();
324 canvas->save();
325 }
326
327 /**
328 * A canvas that records the number of saves, saveLayers and restores.
329 */
330 class SaveCountingCanvas : public SkCanvas {
331 public:
SaveCountingCanvas(int width,int height)332 SaveCountingCanvas(int width, int height)
333 : INHERITED(width, height)
334 , fSaveCount(0)
335 , fSaveLayerCount(0)
336 , fRestoreCount(0){
337 }
338
getSaveLayerStrategy(const SaveLayerRec & rec)339 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
340 ++fSaveLayerCount;
341 return this->INHERITED::getSaveLayerStrategy(rec);
342 }
343
willSave()344 void willSave() override {
345 ++fSaveCount;
346 this->INHERITED::willSave();
347 }
348
willRestore()349 void willRestore() override {
350 ++fRestoreCount;
351 this->INHERITED::willRestore();
352 }
353
getSaveCount() const354 unsigned int getSaveCount() const { return fSaveCount; }
getSaveLayerCount() const355 unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
getRestoreCount() const356 unsigned int getRestoreCount() const { return fRestoreCount; }
357
358 private:
359 unsigned int fSaveCount;
360 unsigned int fSaveLayerCount;
361 unsigned int fRestoreCount;
362
363 typedef SkCanvas INHERITED;
364 };
365
check_save_state(skiatest::Reporter * reporter,SkPicture * picture,unsigned int numSaves,unsigned int numSaveLayers,unsigned int numRestores)366 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
367 unsigned int numSaves, unsigned int numSaveLayers,
368 unsigned int numRestores) {
369 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
370 SkScalarCeilToInt(picture->cullRect().height()));
371
372 picture->playback(&canvas);
373
374 // Optimizations may have removed these,
375 // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
376 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
377 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
378 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
379 }
380
381 // This class exists so SkPicture can friend it and give it access to
382 // the 'partialReplay' method.
383 class SkPictureRecorderReplayTester {
384 public:
Copy(SkPictureRecorder * recorder)385 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
386 SkPictureRecorder recorder2;
387
388 SkCanvas* canvas = recorder2.beginRecording(10, 10);
389
390 recorder->partialReplay(canvas);
391
392 return recorder2.finishRecordingAsPicture();
393 }
394 };
395
create_imbalance(SkCanvas * canvas)396 static void create_imbalance(SkCanvas* canvas) {
397 SkRect clipRect = SkRect::MakeWH(2, 2);
398 SkRect drawRect = SkRect::MakeWH(10, 10);
399 canvas->save();
400 canvas->clipRect(clipRect, kReplace_SkClipOp);
401 canvas->translate(1.0f, 1.0f);
402 SkPaint p;
403 p.setColor(SK_ColorGREEN);
404 canvas->drawRect(drawRect, p);
405 // no restore
406 }
407
408 // This tests that replaying a potentially unbalanced picture into a canvas
409 // doesn't affect the canvas' save count or matrix/clip state.
check_balance(skiatest::Reporter * reporter,SkPicture * picture)410 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
411 SkBitmap bm;
412 bm.allocN32Pixels(4, 3);
413 SkCanvas canvas(bm);
414
415 int beforeSaveCount = canvas.getSaveCount();
416
417 SkMatrix beforeMatrix = canvas.getTotalMatrix();
418
419 SkRect beforeClip = canvas.getLocalClipBounds();
420
421 canvas.drawPicture(picture);
422
423 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
424 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
425
426 SkRect afterClip = canvas.getLocalClipBounds();
427
428 REPORTER_ASSERT(reporter, afterClip == beforeClip);
429 }
430
431 // Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay,reporter)432 DEF_TEST(PictureRecorder_replay, reporter) {
433 // check save/saveLayer state
434 {
435 SkPictureRecorder recorder;
436
437 SkCanvas* canvas = recorder.beginRecording(10, 10);
438
439 canvas->saveLayer(nullptr, nullptr);
440
441 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
442
443 // The extra save and restore comes from the Copy process.
444 check_save_state(reporter, copy.get(), 2, 1, 3);
445
446 canvas->saveLayer(nullptr, nullptr);
447
448 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
449
450 check_save_state(reporter, final.get(), 1, 2, 3);
451
452 // The copy shouldn't pick up any operations added after it was made
453 check_save_state(reporter, copy.get(), 2, 1, 3);
454 }
455
456 // (partially) check leakage of draw ops
457 {
458 SkPictureRecorder recorder;
459
460 SkCanvas* canvas = recorder.beginRecording(10, 10);
461
462 SkRect r = SkRect::MakeWH(5, 5);
463 SkPaint p;
464
465 canvas->drawRect(r, p);
466
467 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
468
469 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
470
471 SkBitmap bm;
472 make_bm(&bm, 10, 10, SK_ColorRED, true);
473
474 r.offset(5.0f, 5.0f);
475 canvas->drawBitmapRect(bm, r, nullptr);
476
477 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
478 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
479
480 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
481
482 // The snapshot shouldn't pick up any operations added after it was made
483 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
484 }
485
486 // Recreate the Android partialReplay test case
487 {
488 SkPictureRecorder recorder;
489
490 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
491 create_imbalance(canvas);
492
493 int expectedSaveCount = canvas->getSaveCount();
494
495 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
496 check_balance(reporter, copy.get());
497
498 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
499
500 // End the recording of source to test the picture finalization
501 // process isn't complicated by the partialReplay step
502 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
503 }
504 }
505
test_unbalanced_save_restores(skiatest::Reporter * reporter)506 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
507 SkCanvas testCanvas(100, 100);
508 set_canvas_to_save_count_4(&testCanvas);
509
510 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
511
512 SkPaint paint;
513 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
514
515 SkPictureRecorder recorder;
516
517 {
518 // Create picture with 2 unbalanced saves
519 SkCanvas* canvas = recorder.beginRecording(100, 100);
520 canvas->save();
521 canvas->translate(10, 10);
522 canvas->drawRect(rect, paint);
523 canvas->save();
524 canvas->translate(10, 10);
525 canvas->drawRect(rect, paint);
526 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
527
528 testCanvas.drawPicture(extraSavePicture);
529 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
530 }
531
532 set_canvas_to_save_count_4(&testCanvas);
533
534 {
535 // Create picture with 2 unbalanced restores
536 SkCanvas* canvas = recorder.beginRecording(100, 100);
537 canvas->save();
538 canvas->translate(10, 10);
539 canvas->drawRect(rect, paint);
540 canvas->save();
541 canvas->translate(10, 10);
542 canvas->drawRect(rect, paint);
543 canvas->restore();
544 canvas->restore();
545 canvas->restore();
546 canvas->restore();
547 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
548
549 testCanvas.drawPicture(extraRestorePicture);
550 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
551 }
552
553 set_canvas_to_save_count_4(&testCanvas);
554
555 {
556 SkCanvas* canvas = recorder.beginRecording(100, 100);
557 canvas->translate(10, 10);
558 canvas->drawRect(rect, paint);
559 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
560
561 testCanvas.drawPicture(noSavePicture);
562 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
563 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
564 }
565 }
566
test_peephole()567 static void test_peephole() {
568 SkRandom rand;
569
570 SkPictureRecorder recorder;
571
572 for (int j = 0; j < 100; j++) {
573 SkRandom rand2(rand); // remember the seed
574
575 SkCanvas* canvas = recorder.beginRecording(100, 100);
576
577 for (int i = 0; i < 1000; ++i) {
578 rand_op(canvas, rand);
579 }
580 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
581
582 rand = rand2;
583 }
584
585 {
586 SkCanvas* canvas = recorder.beginRecording(100, 100);
587 SkRect rect = SkRect::MakeWH(50, 50);
588
589 for (int i = 0; i < 100; ++i) {
590 canvas->save();
591 }
592 while (canvas->getSaveCount() > 1) {
593 canvas->clipRect(rect);
594 canvas->restore();
595 }
596 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
597 }
598 }
599
600 #ifndef SK_DEBUG
601 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
602 // should never do this.
test_bad_bitmap()603 static void test_bad_bitmap() {
604 // This bitmap has a width and height but no pixels. As a result, attempting to record it will
605 // fail.
606 SkBitmap bm;
607 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
608 SkPictureRecorder recorder;
609 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
610 recordingCanvas->drawBitmap(bm, 0, 0);
611 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
612
613 SkCanvas canvas;
614 canvas.drawPicture(picture);
615 }
616 #endif
617
test_clip_bound_opt(skiatest::Reporter * reporter)618 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
619 // Test for crbug.com/229011
620 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
621 SkIntToScalar(2), SkIntToScalar(2));
622 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
623 SkIntToScalar(1), SkIntToScalar(1));
624 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
625 SkIntToScalar(1), SkIntToScalar(1));
626
627 SkPath invPath;
628 invPath.addOval(rect1);
629 invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
630 SkPath path;
631 path.addOval(rect2);
632 SkPath path2;
633 path2.addOval(rect3);
634 SkIRect clipBounds;
635 SkPictureRecorder recorder;
636
637 // Testing conservative-raster-clip that is enabled by PictureRecord
638 {
639 SkCanvas* canvas = recorder.beginRecording(10, 10);
640 canvas->clipPath(invPath);
641 clipBounds = canvas->getDeviceClipBounds();
642 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
643 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
644 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
645 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
646 }
647 {
648 SkCanvas* canvas = recorder.beginRecording(10, 10);
649 canvas->clipPath(path);
650 canvas->clipPath(invPath);
651 clipBounds = canvas->getDeviceClipBounds();
652 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
653 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
654 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
655 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
656 }
657 {
658 SkCanvas* canvas = recorder.beginRecording(10, 10);
659 canvas->clipPath(path);
660 canvas->clipPath(invPath, kUnion_SkClipOp);
661 clipBounds = canvas->getDeviceClipBounds();
662 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
663 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
664 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
665 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
666 }
667 {
668 SkCanvas* canvas = recorder.beginRecording(10, 10);
669 canvas->clipPath(path, kDifference_SkClipOp);
670 clipBounds = canvas->getDeviceClipBounds();
671 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
672 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
673 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
674 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
675 }
676 {
677 SkCanvas* canvas = recorder.beginRecording(10, 10);
678 canvas->clipPath(path, kReverseDifference_SkClipOp);
679 clipBounds = canvas->getDeviceClipBounds();
680 // True clip is actually empty in this case, but the best
681 // determination we can make using only bounds as input is that the
682 // clip is included in the bounds of 'path'.
683 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
684 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
685 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
686 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
687 }
688 {
689 SkCanvas* canvas = recorder.beginRecording(10, 10);
690 canvas->clipPath(path, kIntersect_SkClipOp);
691 canvas->clipPath(path2, kXOR_SkClipOp);
692 clipBounds = canvas->getDeviceClipBounds();
693 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
694 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
695 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
696 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
697 }
698 }
699
test_cull_rect_reset(skiatest::Reporter * reporter)700 static void test_cull_rect_reset(skiatest::Reporter* reporter) {
701 SkPictureRecorder recorder;
702 SkRect bounds = SkRect::MakeWH(10, 10);
703 SkRTreeFactory factory;
704 SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
705 bounds = SkRect::MakeWH(100, 100);
706 SkPaint paint;
707 canvas->drawRect(bounds, paint);
708 canvas->drawRect(bounds, paint);
709 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
710 const SkBigPicture* picture = p->asSkBigPicture();
711 REPORTER_ASSERT(reporter, picture);
712
713 SkRect finalCullRect = picture->cullRect();
714 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
715 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
716 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
717 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
718
719 const SkBBoxHierarchy* pictureBBH = picture->bbh();
720 SkRect bbhCullRect = pictureBBH->getRootBound();
721 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
722 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
723 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
724 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
725 }
726
727
728 /**
729 * A canvas that records the number of clip commands.
730 */
731 class ClipCountingCanvas : public SkCanvas {
732 public:
ClipCountingCanvas(int width,int height)733 ClipCountingCanvas(int width, int height)
734 : INHERITED(width, height)
735 , fClipCount(0){
736 }
737
onClipRect(const SkRect & r,SkClipOp op,ClipEdgeStyle edgeStyle)738 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
739 fClipCount += 1;
740 this->INHERITED::onClipRect(r, op, edgeStyle);
741 }
742
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)743 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
744 fClipCount += 1;
745 this->INHERITED::onClipRRect(rrect, op, edgeStyle);
746 }
747
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)748 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
749 fClipCount += 1;
750 this->INHERITED::onClipPath(path, op, edgeStyle);
751 }
752
onClipRegion(const SkRegion & deviceRgn,SkClipOp op)753 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
754 fClipCount += 1;
755 this->INHERITED::onClipRegion(deviceRgn, op);
756 }
757
getClipCount() const758 unsigned getClipCount() const { return fClipCount; }
759
760 private:
761 unsigned fClipCount;
762
763 typedef SkCanvas INHERITED;
764 };
765
test_clip_expansion(skiatest::Reporter * reporter)766 static void test_clip_expansion(skiatest::Reporter* reporter) {
767 SkPictureRecorder recorder;
768 SkCanvas* canvas = recorder.beginRecording(10, 10);
769
770 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
771 // The following expanding clip should not be skipped.
772 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
773 // Draw something so the optimizer doesn't just fold the world.
774 SkPaint p;
775 p.setColor(SK_ColorBLUE);
776 canvas->drawPaint(p);
777 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
778
779 ClipCountingCanvas testCanvas(10, 10);
780 picture->playback(&testCanvas);
781
782 // Both clips should be present on playback.
783 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
784 }
785
test_hierarchical(skiatest::Reporter * reporter)786 static void test_hierarchical(skiatest::Reporter* reporter) {
787 SkBitmap bm;
788 make_bm(&bm, 10, 10, SK_ColorRED, true);
789
790 SkPictureRecorder recorder;
791
792 recorder.beginRecording(10, 10);
793 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
794 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
795
796 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
797 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
798 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
799
800 {
801 SkCanvas* canvas = recorder.beginRecording(10, 10);
802 canvas->drawPicture(childPlain);
803 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
804 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
805 }
806 {
807 SkCanvas* canvas = recorder.beginRecording(10, 10);
808 canvas->drawPicture(childWithBitmap);
809 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
810 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
811 }
812 {
813 SkCanvas* canvas = recorder.beginRecording(10, 10);
814 canvas->drawBitmap(bm, 0, 0);
815 canvas->drawPicture(childPlain);
816 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
817 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
818 }
819 {
820 SkCanvas* canvas = recorder.beginRecording(10, 10);
821 canvas->drawBitmap(bm, 0, 0);
822 canvas->drawPicture(childWithBitmap);
823 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
824 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
825 }
826 }
827
test_gen_id(skiatest::Reporter * reporter)828 static void test_gen_id(skiatest::Reporter* reporter) {
829
830 SkPictureRecorder recorder;
831 recorder.beginRecording(0, 0);
832 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
833
834 // Empty pictures should still have a valid ID
835 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
836
837 SkCanvas* canvas = recorder.beginRecording(1, 1);
838 canvas->drawColor(SK_ColorWHITE);
839 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
840 // picture should have a non-zero id after recording
841 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
842
843 // both pictures should have different ids
844 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
845 }
846
test_typeface(skiatest::Reporter * reporter)847 static void test_typeface(skiatest::Reporter* reporter) {
848 SkPictureRecorder recorder;
849 SkCanvas* canvas = recorder.beginRecording(10, 10);
850 SkPaint paint;
851 paint.setTypeface(SkTypeface::MakeFromName("Arial",
852 SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
853 canvas->drawText("Q", 1, 0, 10, paint);
854 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
855 SkDynamicMemoryWStream stream;
856 picture->serialize(&stream);
857 }
858
DEF_TEST(Picture,reporter)859 DEF_TEST(Picture, reporter) {
860 test_typeface(reporter);
861 #ifdef SK_DEBUG
862 test_deleting_empty_picture();
863 test_serializing_empty_picture();
864 #else
865 test_bad_bitmap();
866 #endif
867 test_unbalanced_save_restores(reporter);
868 test_peephole();
869 #if SK_SUPPORT_GPU
870 test_gpu_veto(reporter);
871 #endif
872 test_images_are_found_by_willPlayBackBitmaps(reporter);
873 test_analysis(reporter);
874 test_clip_bound_opt(reporter);
875 test_clip_expansion(reporter);
876 test_hierarchical(reporter);
877 test_gen_id(reporter);
878 test_cull_rect_reset(reporter);
879 }
880
draw_bitmaps(const SkBitmap bitmap,SkCanvas * canvas)881 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
882 const SkPaint paint;
883 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
884 const SkIRect irect = { 2, 2, 3, 3 };
885 int divs[] = { 2, 3 };
886 SkCanvas::Lattice lattice;
887 lattice.fXCount = lattice.fYCount = 2;
888 lattice.fXDivs = lattice.fYDivs = divs;
889
890 // Don't care what these record, as long as they're legal.
891 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
892 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
893 canvas->drawBitmapNine(bitmap, irect, rect, &paint);
894 canvas->drawBitmap(bitmap, 1, 1); // drawSprite
895 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
896 }
897
test_draw_bitmaps(SkCanvas * canvas)898 static void test_draw_bitmaps(SkCanvas* canvas) {
899 SkBitmap empty;
900 draw_bitmaps(empty, canvas);
901 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
902 draw_bitmaps(empty, canvas);
903 }
904
DEF_TEST(Picture_EmptyBitmap,r)905 DEF_TEST(Picture_EmptyBitmap, r) {
906 SkPictureRecorder recorder;
907 test_draw_bitmaps(recorder.beginRecording(10, 10));
908 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
909 }
910
DEF_TEST(Canvas_EmptyBitmap,r)911 DEF_TEST(Canvas_EmptyBitmap, r) {
912 SkBitmap dst;
913 dst.allocN32Pixels(10, 10);
914 SkCanvas canvas(dst);
915
916 test_draw_bitmaps(&canvas);
917 }
918
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore,reporter)919 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
920 // This test is from crbug.com/344987.
921 // The commands are:
922 // saveLayer with paint that modifies alpha
923 // drawBitmapRect
924 // drawBitmapRect
925 // restore
926 // The bug was that this structure was modified so that:
927 // - The saveLayer and restore were eliminated
928 // - The alpha was only applied to the first drawBitmapRectToRect
929
930 // This test draws blue and red squares inside a 50% transparent
931 // layer. Both colours should show up muted.
932 // When the bug is present, the red square (the second bitmap)
933 // shows upwith full opacity.
934
935 SkBitmap blueBM;
936 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
937 SkBitmap redBM;
938 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
939 SkPaint semiTransparent;
940 semiTransparent.setAlpha(0x80);
941
942 SkPictureRecorder recorder;
943 SkCanvas* canvas = recorder.beginRecording(100, 100);
944 canvas->drawColor(0);
945
946 canvas->saveLayer(0, &semiTransparent);
947 canvas->drawBitmap(blueBM, 25, 25);
948 canvas->drawBitmap(redBM, 50, 50);
949 canvas->restore();
950
951 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
952
953 // Now replay the picture back on another canvas
954 // and check a couple of its pixels.
955 SkBitmap replayBM;
956 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
957 SkCanvas replayCanvas(replayBM);
958 picture->playback(&replayCanvas);
959 replayCanvas.flush();
960
961 // With the bug present, at (55, 55) we would get a fully opaque red
962 // intead of a dark red.
963 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
964 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
965 }
966
967 struct CountingBBH : public SkBBoxHierarchy {
968 mutable int searchCalls;
969 SkRect rootBound;
970
CountingBBHCountingBBH971 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
972
searchCountingBBH973 void search(const SkRect& query, SkTDArray<int>* results) const override {
974 this->searchCalls++;
975 }
976
insertCountingBBH977 void insert(const SkRect[], int) override {}
bytesUsedCountingBBH978 virtual size_t bytesUsed() const override { return 0; }
getRootBoundCountingBBH979 SkRect getRootBound() const override { return rootBound; }
980 };
981
982 class SpoonFedBBHFactory : public SkBBHFactory {
983 public:
SpoonFedBBHFactory(SkBBoxHierarchy * bbh)984 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
operator ()(const SkRect &) const985 SkBBoxHierarchy* operator()(const SkRect&) const override {
986 return SkRef(fBBH);
987 }
988 private:
989 SkBBoxHierarchy* fBBH;
990 };
991
992 // When the canvas clip covers the full picture, we don't need to call the BBH.
DEF_TEST(Picture_SkipBBH,r)993 DEF_TEST(Picture_SkipBBH, r) {
994 SkRect bound = SkRect::MakeWH(320, 240);
995 CountingBBH bbh(bound);
996 SpoonFedBBHFactory factory(&bbh);
997
998 SkPictureRecorder recorder;
999 SkCanvas* c = recorder.beginRecording(bound, &factory);
1000 // Record a few ops so we don't hit a small- or empty- picture optimization.
1001 c->drawRect(bound, SkPaint());
1002 c->drawRect(bound, SkPaint());
1003 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1004
1005 SkCanvas big(640, 480), small(300, 200);
1006
1007 picture->playback(&big);
1008 REPORTER_ASSERT(r, bbh.searchCalls == 0);
1009
1010 picture->playback(&small);
1011 REPORTER_ASSERT(r, bbh.searchCalls == 1);
1012 }
1013
DEF_TEST(Picture_BitmapLeak,r)1014 DEF_TEST(Picture_BitmapLeak, r) {
1015 SkBitmap mut, immut;
1016 mut.allocN32Pixels(300, 200);
1017 immut.allocN32Pixels(300, 200);
1018 immut.setImmutable();
1019 SkASSERT(!mut.isImmutable());
1020 SkASSERT(immut.isImmutable());
1021
1022 // No one can hold a ref on our pixels yet.
1023 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1024 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1025
1026 sk_sp<SkPicture> pic;
1027 {
1028 // we want the recorder to go out of scope before our subsequent checks, so we
1029 // place it inside local braces.
1030 SkPictureRecorder rec;
1031 SkCanvas* canvas = rec.beginRecording(1920, 1200);
1032 canvas->drawBitmap(mut, 0, 0);
1033 canvas->drawBitmap(immut, 800, 600);
1034 pic = rec.finishRecordingAsPicture();
1035 }
1036
1037 // The picture shares the immutable pixels but copies the mutable ones.
1038 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1039 REPORTER_ASSERT(r, !immut.pixelRef()->unique());
1040
1041 // When the picture goes away, it's just our bitmaps holding the refs.
1042 pic = nullptr;
1043 REPORTER_ASSERT(r, mut.pixelRef()->unique());
1044 REPORTER_ASSERT(r, immut.pixelRef()->unique());
1045 }
1046
1047 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
DEF_TEST(Picture_getRecordingCanvas,r)1048 DEF_TEST(Picture_getRecordingCanvas, r) {
1049 SkPictureRecorder rec;
1050 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1051 for (int i = 0; i < 3; i++) {
1052 rec.beginRecording(100, 100);
1053 REPORTER_ASSERT(r, rec.getRecordingCanvas());
1054 rec.finishRecordingAsPicture();
1055 REPORTER_ASSERT(r, !rec.getRecordingCanvas());
1056 }
1057 }
1058
DEF_TEST(MiniRecorderLeftHanging,r)1059 DEF_TEST(MiniRecorderLeftHanging, r) {
1060 // Any shader or other ref-counted effect will do just fine here.
1061 SkPaint paint;
1062 paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
1063
1064 SkMiniRecorder rec;
1065 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
1066 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
1067 }
1068
DEF_TEST(Picture_preserveCullRect,r)1069 DEF_TEST(Picture_preserveCullRect, r) {
1070 SkPictureRecorder recorder;
1071
1072 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
1073 c->clear(SK_ColorCYAN);
1074
1075 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1076 SkDynamicMemoryWStream wstream;
1077 picture->serialize(&wstream);
1078
1079 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
1080 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
1081
1082 REPORTER_ASSERT(r, deserializedPicture != nullptr);
1083 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
1084 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
1085 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
1086 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
1087 }
1088
1089 #if SK_SUPPORT_GPU
1090
DEF_TEST(PictureGpuAnalyzer,r)1091 DEF_TEST(PictureGpuAnalyzer, r) {
1092 SkPictureRecorder recorder;
1093
1094 {
1095 SkCanvas* canvas = recorder.beginRecording(10, 10);
1096 SkPaint paint;
1097 SkScalar intervals [] = { 10, 20 };
1098 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
1099
1100 for (int i = 0; i < 50; ++i) {
1101 canvas->drawRect(SkRect::MakeWH(10, 10), paint);
1102 }
1103 }
1104 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
1105
1106 SkPictureGpuAnalyzer analyzer;
1107 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1108
1109 analyzer.analyzePicture(vetoPicture.get());
1110 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1111
1112 analyzer.reset();
1113 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1114
1115 recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
1116 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
1117
1118 analyzer.analyzePicture(nestedVetoPicture.get());
1119 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1120
1121 analyzer.reset();
1122
1123 const SkPath convexClip = make_convex_path();
1124 const SkPath concaveClip = make_concave_path();
1125 for (int i = 0; i < 50; ++i) {
1126 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false);
1127 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true);
1128 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false);
1129 }
1130 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
1131
1132 for (int i = 0; i < 50; ++i) {
1133 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true);
1134 }
1135 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
1136 }
1137
1138 #endif // SK_SUPPORT_GPU
1139
1140 ///////////////////////////////////////////////////////////////////////////////////////////////////
1141
1142 // Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
1143 #if 0
1144 static void empty_ops(SkCanvas* canvas) {
1145 }
1146 static void clip_ops(SkCanvas* canvas) {
1147 canvas->save();
1148 canvas->clipRect(SkRect::MakeWH(20, 20));
1149 canvas->restore();
1150 }
1151 static void matrix_ops(SkCanvas* canvas) {
1152 canvas->save();
1153 canvas->scale(2, 3);
1154 canvas->restore();
1155 }
1156 static void matrixclip_ops(SkCanvas* canvas) {
1157 canvas->save();
1158 canvas->scale(2, 3);
1159 canvas->clipRect(SkRect::MakeWH(20, 20));
1160 canvas->restore();
1161 }
1162 typedef void (*CanvasProc)(SkCanvas*);
1163
1164 // Test the kReturnNullForEmpty_FinishFlag option when recording
1165 //
1166 DEF_TEST(Picture_RecordEmpty, r) {
1167 const SkRect cull = SkRect::MakeWH(100, 100);
1168
1169 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
1170
1171 for (auto proc : procs) {
1172 {
1173 SkPictureRecorder rec;
1174 proc(rec.beginRecording(cull));
1175 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
1176 REPORTER_ASSERT(r, pic.get());
1177 REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
1178 }
1179 {
1180 SkPictureRecorder rec;
1181 proc(rec.beginRecording(cull));
1182 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
1183 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1184 REPORTER_ASSERT(r, !pic.get());
1185 }
1186 {
1187 SkPictureRecorder rec;
1188 proc(rec.beginRecording(cull));
1189 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
1190 REPORTER_ASSERT(r, dr.get());
1191 }
1192 {
1193 SkPictureRecorder rec;
1194 proc(rec.beginRecording(cull));
1195 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
1196 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
1197 REPORTER_ASSERT(r, !dr.get());
1198 }
1199 }
1200 }
1201 #endif
1202
1203