• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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