xref: /aosp_15_r20/external/skia/tests/PictureTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "include/core/SkBBHFactory.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkClipOp.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkFont.h"
15 #include "include/core/SkFontStyle.h"
16 #include "include/core/SkImage.h" // IWYU pragma: keep
17 #include "include/core/SkImageInfo.h"
18 #include "include/core/SkMatrix.h"
19 #include "include/core/SkPaint.h"
20 #include "include/core/SkPath.h"
21 #include "include/core/SkPathTypes.h"
22 #include "include/core/SkPicture.h"
23 #include "include/core/SkPictureRecorder.h"
24 #include "include/core/SkPixelRef.h"
25 #include "include/core/SkRect.h"
26 #include "include/core/SkRefCnt.h"
27 #include "include/core/SkSamplingOptions.h"
28 #include "include/core/SkScalar.h"
29 #include "include/core/SkStream.h"
30 #include "include/core/SkTypeface.h"
31 #include "include/core/SkTypes.h"
32 #include "src/base/SkRandom.h"
33 #include "src/core/SkBigPicture.h"
34 #include "src/core/SkPicturePriv.h"
35 #include "src/core/SkRectPriv.h"
36 #include "tests/Test.h"
37 #include "tools/fonts/FontToolUtils.h"
38 
39 #include <cstddef>
40 #include <memory>
41 #include <vector>
42 
43 class SkRRect;
44 class SkRegion;
45 
make_bm(SkBitmap * bm,int w,int h,SkColor color,bool immutable)46 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
47     bm->allocN32Pixels(w, h);
48     bm->eraseColor(color);
49     if (immutable) {
50         bm->setImmutable();
51     }
52 }
53 
54 #ifdef SK_DEBUG
55 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
56 // in debug mode, so only run in debug mode.
test_deleting_empty_picture()57 static void test_deleting_empty_picture() {
58     SkPictureRecorder recorder;
59     // Creates an SkPictureRecord
60     recorder.beginRecording(0, 0);
61     // Turns that into an SkPicture
62     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
63     // Ceates a new SkPictureRecord
64     recorder.beginRecording(0, 0);
65 }
66 
67 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
test_serializing_empty_picture()68 static void test_serializing_empty_picture() {
69     SkPictureRecorder recorder;
70     recorder.beginRecording(0, 0);
71     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
72     SkDynamicMemoryWStream stream;
73     picture->serialize(&stream, nullptr);  // default SkSerialProcs
74 }
75 #endif
76 
rand_op(SkCanvas * canvas,SkRandom & rand)77 static void rand_op(SkCanvas* canvas, SkRandom& rand) {
78     SkPaint paint;
79     SkRect rect = SkRect::MakeWH(50, 50);
80 
81     SkScalar unit = rand.nextUScalar1();
82     if (unit <= 0.3) {
83 //        SkDebugf("save\n");
84         canvas->save();
85     } else if (unit <= 0.6) {
86 //        SkDebugf("restore\n");
87         canvas->restore();
88     } else if (unit <= 0.9) {
89 //        SkDebugf("clip\n");
90         canvas->clipRect(rect);
91     } else {
92 //        SkDebugf("draw\n");
93         canvas->drawPaint(paint);
94     }
95 }
96 
set_canvas_to_save_count_4(SkCanvas * canvas)97 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
98     canvas->restoreToCount(1);
99     canvas->save();
100     canvas->save();
101     canvas->save();
102 }
103 
104 /**
105  * A canvas that records the number of saves, saveLayers and restores.
106  */
107 class SaveCountingCanvas : public SkCanvas {
108 public:
SaveCountingCanvas(int width,int height)109     SaveCountingCanvas(int width, int height)
110         : INHERITED(width, height)
111         , fSaveCount(0)
112         , fSaveLayerCount(0)
113         , fSaveBehindCount(0)
114         , fRestoreCount(0){
115     }
116 
getSaveLayerStrategy(const SaveLayerRec & rec)117     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
118         ++fSaveLayerCount;
119         return this->INHERITED::getSaveLayerStrategy(rec);
120     }
121 
onDoSaveBehind(const SkRect * subset)122     bool onDoSaveBehind(const SkRect* subset) override {
123         ++fSaveBehindCount;
124         return this->INHERITED::onDoSaveBehind(subset);
125     }
126 
willSave()127     void willSave() override {
128         ++fSaveCount;
129         this->INHERITED::willSave();
130     }
131 
willRestore()132     void willRestore() override {
133         ++fRestoreCount;
134         this->INHERITED::willRestore();
135     }
136 
getSaveCount() const137     unsigned int getSaveCount() const { return fSaveCount; }
getSaveLayerCount() const138     unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
getSaveBehindCount() const139     unsigned int getSaveBehindCount() const { return fSaveBehindCount; }
getRestoreCount() const140     unsigned int getRestoreCount() const { return fRestoreCount; }
141 
142 private:
143     unsigned int fSaveCount;
144     unsigned int fSaveLayerCount;
145     unsigned int fSaveBehindCount;
146     unsigned int fRestoreCount;
147 
148     using INHERITED = SkCanvas;
149 };
150 
check_save_state(skiatest::Reporter * reporter,SkPicture * picture,unsigned int numSaves,unsigned int numSaveLayers,unsigned int numRestores)151 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
152                       unsigned int numSaves, unsigned int numSaveLayers,
153                       unsigned int numRestores) {
154     SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
155                               SkScalarCeilToInt(picture->cullRect().height()));
156 
157     picture->playback(&canvas);
158 
159     // Optimizations may have removed these,
160     // so expect to have seen no more than num{Saves,SaveLayers,Restores}.
161     REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
162     REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
163     REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
164 }
165 
166 // This class exists so SkPicture can friend it and give it access to
167 // the 'partialReplay' method.
168 class SkPictureRecorderReplayTester {
169 public:
Copy(SkPictureRecorder * recorder)170     static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
171         SkPictureRecorder recorder2;
172 
173         SkCanvas* canvas = recorder2.beginRecording(10, 10);
174 
175         recorder->partialReplay(canvas);
176 
177         return recorder2.finishRecordingAsPicture();
178     }
179 };
180 
create_imbalance(SkCanvas * canvas)181 static void create_imbalance(SkCanvas* canvas) {
182     SkRect clipRect = SkRect::MakeWH(2, 2);
183     SkRect drawRect = SkRect::MakeWH(10, 10);
184     canvas->save();
185         canvas->clipRect(clipRect, SkClipOp::kIntersect);
186         canvas->translate(1.0f, 1.0f);
187         SkPaint p;
188         p.setColor(SK_ColorGREEN);
189         canvas->drawRect(drawRect, p);
190     // no restore
191 }
192 
193 // This tests that replaying a potentially unbalanced picture into a canvas
194 // doesn't affect the canvas' save count or matrix/clip state.
check_balance(skiatest::Reporter * reporter,SkPicture * picture)195 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
196     SkBitmap bm;
197     bm.allocN32Pixels(4, 3);
198     SkCanvas canvas(bm);
199 
200     int beforeSaveCount = canvas.getSaveCount();
201 
202     SkMatrix beforeMatrix = canvas.getTotalMatrix();
203 
204     SkRect beforeClip = canvas.getLocalClipBounds();
205 
206     canvas.drawPicture(picture);
207 
208     REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
209     REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
210 
211     SkRect afterClip = canvas.getLocalClipBounds();
212 
213     REPORTER_ASSERT(reporter, afterClip == beforeClip);
214 }
215 
216 // Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay,reporter)217 DEF_TEST(PictureRecorder_replay, reporter) {
218     // check save/saveLayer state
219     {
220         SkPictureRecorder recorder;
221 
222         SkCanvas* canvas = recorder.beginRecording(10, 10);
223 
224         canvas->saveLayer(nullptr, nullptr);
225 
226         sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
227 
228         // The extra save and restore comes from the Copy process.
229         check_save_state(reporter, copy.get(), 2, 1, 3);
230 
231         canvas->saveLayer(nullptr, nullptr);
232 
233         sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
234 
235         check_save_state(reporter, final.get(), 1, 2, 3);
236 
237         // The copy shouldn't pick up any operations added after it was made
238         check_save_state(reporter, copy.get(), 2, 1, 3);
239     }
240 
241     // Recreate the Android partialReplay test case
242     {
243         SkPictureRecorder recorder;
244 
245         SkCanvas* canvas = recorder.beginRecording(4, 3);
246         create_imbalance(canvas);
247 
248         int expectedSaveCount = canvas->getSaveCount();
249 
250         sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
251         check_balance(reporter, copy.get());
252 
253         REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
254 
255         // End the recording of source to test the picture finalization
256         // process isn't complicated by the partialReplay step
257         sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
258     }
259 }
260 
test_unbalanced_save_restores(skiatest::Reporter * reporter)261 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
262     SkCanvas testCanvas(100, 100);
263     set_canvas_to_save_count_4(&testCanvas);
264 
265     REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
266 
267     SkPaint paint;
268     SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
269 
270     SkPictureRecorder recorder;
271 
272     {
273         // Create picture with 2 unbalanced saves
274         SkCanvas* canvas = recorder.beginRecording(100, 100);
275         canvas->save();
276         canvas->translate(10, 10);
277         canvas->drawRect(rect, paint);
278         canvas->save();
279         canvas->translate(10, 10);
280         canvas->drawRect(rect, paint);
281         sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
282 
283         testCanvas.drawPicture(extraSavePicture);
284         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
285     }
286 
287     set_canvas_to_save_count_4(&testCanvas);
288 
289     {
290         // Create picture with 2 unbalanced restores
291         SkCanvas* canvas = recorder.beginRecording(100, 100);
292         canvas->save();
293         canvas->translate(10, 10);
294         canvas->drawRect(rect, paint);
295         canvas->save();
296         canvas->translate(10, 10);
297         canvas->drawRect(rect, paint);
298         canvas->restore();
299         canvas->restore();
300         canvas->restore();
301         canvas->restore();
302         sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
303 
304         testCanvas.drawPicture(extraRestorePicture);
305         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
306     }
307 
308     set_canvas_to_save_count_4(&testCanvas);
309 
310     {
311         SkCanvas* canvas = recorder.beginRecording(100, 100);
312         canvas->translate(10, 10);
313         canvas->drawRect(rect, paint);
314         sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
315 
316         testCanvas.drawPicture(noSavePicture);
317         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
318         REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
319     }
320 }
321 
test_peephole()322 static void test_peephole() {
323     SkRandom rand;
324 
325     SkPictureRecorder recorder;
326 
327     for (int j = 0; j < 100; j++) {
328         SkRandom rand2(rand); // remember the seed
329 
330         SkCanvas* canvas = recorder.beginRecording(100, 100);
331 
332         for (int i = 0; i < 1000; ++i) {
333             rand_op(canvas, rand);
334         }
335         sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
336 
337         rand = rand2;
338     }
339 
340     {
341         SkCanvas* canvas = recorder.beginRecording(100, 100);
342         SkRect rect = SkRect::MakeWH(50, 50);
343 
344         for (int i = 0; i < 100; ++i) {
345             canvas->save();
346         }
347         while (canvas->getSaveCount() > 1) {
348             canvas->clipRect(rect);
349             canvas->restore();
350         }
351         sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
352     }
353 }
354 
test_bad_bitmap(skiatest::Reporter * reporter)355 static void test_bad_bitmap(skiatest::Reporter* reporter) {
356     // missing pixels should return null for image
357     SkBitmap bm;
358     bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
359     auto img = bm.asImage();
360     REPORTER_ASSERT(reporter, !img);
361 
362     // make sure we don't crash on a null image
363     SkPictureRecorder recorder;
364     SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
365     recordingCanvas->drawImage(nullptr, 0, 0);
366     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
367 
368     SkCanvas canvas;
369     canvas.drawPicture(picture);
370 }
371 
test_clip_bound_opt(skiatest::Reporter * reporter)372 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
373     // Test for crbug.com/229011
374     SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
375                                     SkIntToScalar(2), SkIntToScalar(2));
376     SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
377                                     SkIntToScalar(1), SkIntToScalar(1));
378     SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
379                                     SkIntToScalar(1), SkIntToScalar(1));
380 
381     SkPath invPath;
382     invPath.addOval(rect1);
383     invPath.setFillType(SkPathFillType::kInverseEvenOdd);
384     SkPath path;
385     path.addOval(rect2);
386     SkPath path2;
387     path2.addOval(rect3);
388     SkIRect clipBounds;
389     SkPictureRecorder recorder;
390 
391     // Testing conservative-raster-clip that is enabled by PictureRecord
392     {
393         SkCanvas* canvas = recorder.beginRecording(10, 10);
394         canvas->clipPath(invPath);
395         clipBounds = canvas->getDeviceClipBounds();
396         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
397         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
398         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
399         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
400     }
401     {
402         SkCanvas* canvas = recorder.beginRecording(10, 10);
403         canvas->clipPath(path);
404         canvas->clipPath(invPath);
405         clipBounds = canvas->getDeviceClipBounds();
406         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
407         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
408         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
409         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
410     }
411     {
412         SkCanvas* canvas = recorder.beginRecording(10, 10);
413         canvas->clipPath(path, SkClipOp::kDifference);
414         clipBounds = canvas->getDeviceClipBounds();
415         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
416         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
417         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
418         REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
419     }
420     {
421         SkCanvas* canvas = recorder.beginRecording(10, 10);
422         canvas->clipPath(path, SkClipOp::kIntersect);
423         canvas->clipPath(path2, SkClipOp::kDifference);
424         clipBounds = canvas->getDeviceClipBounds();
425         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
426         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
427         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
428         REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
429     }
430 }
431 
test_cull_rect_reset(skiatest::Reporter * reporter)432 static void test_cull_rect_reset(skiatest::Reporter* reporter) {
433     SkPictureRecorder recorder;
434     SkRect bounds = SkRect::MakeWH(10, 10);
435     SkRTreeFactory factory;
436     SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
437     bounds = SkRect::MakeWH(100, 100);
438     SkPaint paint;
439     canvas->drawRect(bounds, paint);
440     canvas->drawRect(bounds, paint);
441     sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
442     const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p);
443     REPORTER_ASSERT(reporter, picture);
444 
445     SkRect finalCullRect = picture->cullRect();
446     REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
447     REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
448     REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
449     REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
450 }
451 
452 
453 /**
454  * A canvas that records the number of clip commands.
455  */
456 class ClipCountingCanvas : public SkCanvas {
457 public:
ClipCountingCanvas(int width,int height)458     ClipCountingCanvas(int width, int height)
459         : INHERITED(width, height)
460         , fClipCount(0){
461     }
462 
onClipRect(const SkRect & r,SkClipOp op,ClipEdgeStyle edgeStyle)463     void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
464         fClipCount += 1;
465         this->INHERITED::onClipRect(r, op, edgeStyle);
466     }
467 
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)468     void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
469         fClipCount += 1;
470         this->INHERITED::onClipRRect(rrect, op, edgeStyle);
471     }
472 
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)473     void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
474         fClipCount += 1;
475         this->INHERITED::onClipPath(path, op, edgeStyle);
476     }
477 
onClipRegion(const SkRegion & deviceRgn,SkClipOp op)478     void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
479         fClipCount += 1;
480         this->INHERITED::onClipRegion(deviceRgn, op);
481     }
482 
getClipCount() const483     unsigned getClipCount() const { return fClipCount; }
484 
485 private:
486     unsigned fClipCount;
487 
488     using INHERITED = SkCanvas;
489 };
490 
test_gen_id(skiatest::Reporter * reporter)491 static void test_gen_id(skiatest::Reporter* reporter) {
492 
493     SkPictureRecorder recorder;
494     recorder.beginRecording(0, 0);
495     sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
496 
497     // Empty pictures should still have a valid ID
498     REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
499 
500     SkCanvas* canvas = recorder.beginRecording(1, 1);
501     canvas->drawColor(SK_ColorWHITE);
502     sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
503     // picture should have a non-zero id after recording
504     REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
505 
506     // both pictures should have different ids
507     REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
508 }
509 
test_typeface(skiatest::Reporter * reporter)510 static void test_typeface(skiatest::Reporter* reporter) {
511     SkPictureRecorder recorder;
512     SkCanvas* canvas = recorder.beginRecording(10, 10);
513     SkFont font(ToolUtils::CreateTestTypeface("Arial", SkFontStyle::Italic()));
514     canvas->drawString("Q", 0, 10, font, SkPaint());
515     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
516     SkDynamicMemoryWStream stream;
517     picture->serialize(&stream, nullptr);  // default SkSerialProcs
518 }
519 
DEF_TEST(Picture,reporter)520 DEF_TEST(Picture, reporter) {
521     test_typeface(reporter);
522 #ifdef SK_DEBUG
523     test_deleting_empty_picture();
524     test_serializing_empty_picture();
525 #endif
526     test_bad_bitmap(reporter);
527     test_unbalanced_save_restores(reporter);
528     test_peephole();
529     test_clip_bound_opt(reporter);
530     test_gen_id(reporter);
531     test_cull_rect_reset(reporter);
532 }
533 
draw_bitmaps(const SkBitmap & bitmap,SkCanvas * canvas)534 static void draw_bitmaps(const SkBitmap& bitmap, SkCanvas* canvas) {
535     const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
536     auto img = bitmap.asImage();
537 
538     // Don't care what these record, as long as they're legal.
539     canvas->drawImage(img, 0.0f, 0.0f);
540     canvas->drawImageRect(img, rect, rect, SkSamplingOptions(), nullptr,
541                           SkCanvas::kStrict_SrcRectConstraint);
542     canvas->drawImage(img, 1, 1);   // drawSprite
543 }
544 
test_draw_bitmaps(SkCanvas * canvas)545 static void test_draw_bitmaps(SkCanvas* canvas) {
546     SkBitmap empty;
547     draw_bitmaps(empty, canvas);
548     empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
549     draw_bitmaps(empty, canvas);
550 }
551 
DEF_TEST(Picture_EmptyBitmap,r)552 DEF_TEST(Picture_EmptyBitmap, r) {
553     SkPictureRecorder recorder;
554     test_draw_bitmaps(recorder.beginRecording(10, 10));
555     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
556 }
557 
DEF_TEST(Canvas_EmptyBitmap,r)558 DEF_TEST(Canvas_EmptyBitmap, r) {
559     SkBitmap dst;
560     dst.allocN32Pixels(10, 10);
561     SkCanvas canvas(dst);
562 
563     test_draw_bitmaps(&canvas);
564 }
565 
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore,reporter)566 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
567     // This test is from crbug.com/344987.
568     // The commands are:
569     //   saveLayer with paint that modifies alpha
570     //     drawBitmapRect
571     //     drawBitmapRect
572     //   restore
573     // The bug was that this structure was modified so that:
574     //  - The saveLayer and restore were eliminated
575     //  - The alpha was only applied to the first drawBitmapRectToRect
576 
577     // This test draws blue and red squares inside a 50% transparent
578     // layer.  Both colours should show up muted.
579     // When the bug is present, the red square (the second bitmap)
580     // shows upwith full opacity.
581 
582     SkBitmap blueBM;
583     make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
584     SkBitmap redBM;
585     make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
586     SkPaint semiTransparent;
587     semiTransparent.setAlpha(0x80);
588 
589     SkPictureRecorder recorder;
590     SkCanvas* canvas = recorder.beginRecording(100, 100);
591     canvas->drawColor(0);
592 
593     canvas->saveLayer(nullptr, &semiTransparent);
594     canvas->drawImage(blueBM.asImage(), 25, 25);
595     canvas->drawImage(redBM.asImage(), 50, 50);
596     canvas->restore();
597 
598     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
599 
600     // Now replay the picture back on another canvas
601     // and check a couple of its pixels.
602     SkBitmap replayBM;
603     make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
604     SkCanvas replayCanvas(replayBM);
605     picture->playback(&replayCanvas);
606 
607     // With the bug present, at (55, 55) we would get a fully opaque red
608     // intead of a dark red.
609     REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
610     REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
611 }
612 
613 struct CountingBBH : public SkBBoxHierarchy {
614     mutable int searchCalls;
615 
CountingBBHCountingBBH616     CountingBBH() : searchCalls(0) {}
617 
searchCountingBBH618     void search(const SkRect& query, std::vector<int>* results) const override {
619         this->searchCalls++;
620     }
621 
insertCountingBBH622     void insert(const SkRect[], int) override {}
bytesUsedCountingBBH623     size_t bytesUsed() const override { return 0; }
624 };
625 
626 class SpoonFedBBHFactory : public SkBBHFactory {
627 public:
SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh)628     explicit SpoonFedBBHFactory(sk_sp<SkBBoxHierarchy> bbh) : fBBH(std::move(bbh)) {}
operator ()() const629     sk_sp<SkBBoxHierarchy> operator()() const override {
630         return fBBH;
631     }
632 private:
633     sk_sp<SkBBoxHierarchy> fBBH;
634 };
635 
636 // When the canvas clip covers the full picture, we don't need to call the BBH.
DEF_TEST(Picture_SkipBBH,r)637 DEF_TEST(Picture_SkipBBH, r) {
638     SkRect bound = SkRect::MakeWH(320, 240);
639 
640     auto bbh = sk_make_sp<CountingBBH>();
641     SpoonFedBBHFactory factory(bbh);
642 
643     SkPictureRecorder recorder;
644     SkCanvas* c = recorder.beginRecording(bound, &factory);
645     // Record a few ops so we don't hit a small- or empty- picture optimization.
646         c->drawRect(bound, SkPaint());
647         c->drawRect(bound, SkPaint());
648     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
649 
650     SkCanvas big(640, 480), small(300, 200);
651 
652     picture->playback(&big);
653     REPORTER_ASSERT(r, bbh->searchCalls == 0);
654 
655     picture->playback(&small);
656     REPORTER_ASSERT(r, bbh->searchCalls == 1);
657 }
658 
DEF_TEST(Picture_BitmapLeak,r)659 DEF_TEST(Picture_BitmapLeak, r) {
660     SkBitmap mut, immut;
661     mut.allocN32Pixels(300, 200);
662     immut.allocN32Pixels(300, 200);
663     immut.setImmutable();
664     SkASSERT(!mut.isImmutable());
665     SkASSERT(immut.isImmutable());
666 
667     // No one can hold a ref on our pixels yet.
668     REPORTER_ASSERT(r, mut.pixelRef()->unique());
669     REPORTER_ASSERT(r, immut.pixelRef()->unique());
670 
671     sk_sp<SkPicture> pic;
672     {
673         // we want the recorder to go out of scope before our subsequent checks, so we
674         // place it inside local braces.
675         SkPictureRecorder rec;
676         SkCanvas* canvas = rec.beginRecording(1920, 1200);
677             canvas->drawImage(mut.asImage(), 0, 0);
678             canvas->drawImage(immut.asImage(), 800, 600);
679         pic = rec.finishRecordingAsPicture();
680     }
681 
682     // The picture shares the immutable pixels but copies the mutable ones.
683     REPORTER_ASSERT(r, mut.pixelRef()->unique());
684     REPORTER_ASSERT(r, !immut.pixelRef()->unique());
685 
686     // When the picture goes away, it's just our bitmaps holding the refs.
687     pic = nullptr;
688     REPORTER_ASSERT(r, mut.pixelRef()->unique());
689     REPORTER_ASSERT(r, immut.pixelRef()->unique());
690 }
691 
692 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
DEF_TEST(Picture_getRecordingCanvas,r)693 DEF_TEST(Picture_getRecordingCanvas, r) {
694     SkPictureRecorder rec;
695     REPORTER_ASSERT(r, !rec.getRecordingCanvas());
696     for (int i = 0; i < 3; i++) {
697         rec.beginRecording(100, 100);
698         REPORTER_ASSERT(r, rec.getRecordingCanvas());
699         rec.finishRecordingAsPicture();
700         REPORTER_ASSERT(r, !rec.getRecordingCanvas());
701     }
702 }
703 
DEF_TEST(Picture_preserveCullRect,r)704 DEF_TEST(Picture_preserveCullRect, r) {
705     SkPictureRecorder recorder;
706 
707     SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
708     c->clear(SK_ColorCYAN);
709 
710     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
711     SkDynamicMemoryWStream wstream;
712     picture->serialize(&wstream, nullptr);  // default SkSerialProcs
713 
714     std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
715     sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
716 
717     REPORTER_ASSERT(r, deserializedPicture != nullptr);
718     REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
719     REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
720     REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
721     REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
722 }
723 
724 
725 // If we record bounded ops into a picture with a big cull and calculate the
726 // bounds of those ops, we should trim down the picture cull to the ops' bounds.
727 // If we're not using an SkBBH, we shouldn't change it.
DEF_TEST(Picture_UpdatedCull_1,r)728 DEF_TEST(Picture_UpdatedCull_1, r) {
729     SkRTreeFactory factory;
730     SkPictureRecorder recorder;
731 
732     auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
733     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
734     auto pic = recorder.finishRecordingAsPicture();
735     REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
736 
737     canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
738     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
739     pic = recorder.finishRecordingAsPicture();
740     REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
741 }
DEF_TEST(Picture_UpdatedCull_2,r)742 DEF_TEST(Picture_UpdatedCull_2, r) {
743     SkRTreeFactory factory;
744     SkPictureRecorder recorder;
745 
746     auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
747     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
748     canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
749     auto pic = recorder.finishRecordingAsPicture();
750     REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
751 
752     canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
753     canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
754     canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
755     pic = recorder.finishRecordingAsPicture();
756     REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
757 }
758 
DEF_TEST(Placeholder,r)759 DEF_TEST(Placeholder, r) {
760     SkRect cull = { 0,0, 10,20 };
761 
762     // Each placeholder is unique.
763     sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
764                      p2 = SkPicture::MakePlaceholder(cull);
765     REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
766     REPORTER_ASSERT(r, p1->cullRect() == cull);
767     REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
768 
769     // Placeholders are never unrolled by SkCanvas (while other small pictures may be).
770     SkPictureRecorder recorder;
771     SkCanvas* canvas = recorder.beginRecording(cull);
772         canvas->drawPicture(p1);
773         canvas->drawPicture(p2);
774     sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
775     REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
776 
777     // Any upper limit when recursing into nested placeholders is fine as long
778     // as it doesn't overflow an int.
779     REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) >=  2);
780     REPORTER_ASSERT(r, pic->approximateOpCount(/*nested?*/true) <= 10);
781 }
782 
DEF_TEST(Picture_empty_serial,reporter)783 DEF_TEST(Picture_empty_serial, reporter) {
784     SkPictureRecorder rec;
785     (void)rec.beginRecording(10, 10);
786     auto pic = rec.finishRecordingAsPicture();
787     REPORTER_ASSERT(reporter, pic);
788 
789     auto data = pic->serialize(); // explicitly testing the default SkSerialProcs
790     REPORTER_ASSERT(reporter, data);
791 
792     auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
793     REPORTER_ASSERT(reporter, pic2);
794 }
795 
796 
DEF_TEST(Picture_drawsNothing,r)797 DEF_TEST(Picture_drawsNothing, r) {
798     // Tests that pic->cullRect().isEmpty() is a good way to test a picture
799     // recorded with an R-tree draws nothing.
800     struct {
801         bool draws_nothing;
802         void (*fn)(SkCanvas*);
803     } cases[] = {
804         {  true, [](SkCanvas* c) {                                                             } },
805         {  true, [](SkCanvas* c) { c->save();                                    c->restore(); } },
806         {  true, [](SkCanvas* c) { c->save(); c->clipRect({0,0,5,5});            c->restore(); } },
807         {  true, [](SkCanvas* c) {            c->clipRect({0,0,5,5});                          } },
808 
809         { false, [](SkCanvas* c) {            c->drawRect({0,0,5,5}, SkPaint{});               } },
810         { false, [](SkCanvas* c) { c->save(); c->drawRect({0,0,5,5}, SkPaint{}); c->restore(); } },
811         { false, [](SkCanvas* c) {
812             c->drawRect({0,0, 5, 5}, SkPaint{});
813             c->drawRect({5,5,10,10}, SkPaint{});
814         }},
815     };
816 
817     for (const auto& c : cases) {
818         SkPictureRecorder rec;
819         SkRTreeFactory factory;
820         c.fn(rec.beginRecording(10,10, &factory));
821         sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
822 
823         REPORTER_ASSERT(r, pic->cullRect().isEmpty() == c.draws_nothing);
824     }
825 }
826 
DEF_TEST(Picture_emptyNestedPictureBug,r)827 DEF_TEST(Picture_emptyNestedPictureBug, r) {
828     const SkRect bounds = {-5000, -5000, 5000, 5000};
829 
830     SkPictureRecorder recorder;
831     SkRTreeFactory factory;
832 
833     // These three pictures should all draw the same but due to bugs they don't:
834     //
835     //   1) inner has enough content that it is recoreded as an SkBigPicture,
836     //      and all its content falls outside the positive/positive quadrant,
837     //      and it is recorded with an R-tree so we contract the cullRect to those bounds;
838     //
839     //   2) middle wraps inner,
840     //      and it its recorded with an R-tree so we update middle's cullRect to inner's;
841     //
842     //   3) outer wraps inner,
843     //      and notices that middle contains only one op, drawPicture(inner),
844     //      so it plays middle back during recording rather than ref'ing middle,
845     //      querying middle's R-tree with its SkCanvas' bounds* {0,0, 5000,5000},
846     //      finding nothing to draw.
847     //
848     //  * The bug was that these bounds were not tracked as {-5000,-5000, 5000,5000}.
849     {
850         SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
851         canvas->translate(-100,-100);
852         canvas->drawRect({0,0,50,50}, SkPaint{});
853     }
854     sk_sp<SkPicture> inner = recorder.finishRecordingAsPicture();
855 
856     recorder.beginRecording(bounds, &factory)->drawPicture(inner);
857     sk_sp<SkPicture> middle = recorder.finishRecordingAsPicture();
858 
859     // This doesn't need &factory to reproduce the bug,
860     // but it's nice to see we come up with the same {-100,-100, -50,-50} bounds.
861     recorder.beginRecording(bounds, &factory)->drawPicture(middle);
862     sk_sp<SkPicture> outer = recorder.finishRecordingAsPicture();
863 
864     REPORTER_ASSERT(r, (inner ->cullRect() == SkRect{-100,-100, -50,-50}));
865     REPORTER_ASSERT(r, (middle->cullRect() == SkRect{-100,-100, -50,-50}));
866     REPORTER_ASSERT(r, (outer ->cullRect() == SkRect{-100,-100, -50,-50}));   // Used to fail.
867 }
868 
DEF_TEST(Picture_fillsBBH,r)869 DEF_TEST(Picture_fillsBBH, r) {
870     // Test empty (0 draws), mini (1 draw), and big (2+) pictures, making sure they fill the BBH.
871     const SkRect rects[] = {
872         { 0, 0, 20,20},
873         {20,20, 40,40},
874     };
875 
876     for (int n = 0; n <= 2; n++) {
877         SkRTreeFactory factory;
878         SkPictureRecorder rec;
879 
880         sk_sp<SkBBoxHierarchy> bbh = factory();
881 
882         SkCanvas* c = rec.beginRecording({0,0, 100,100}, bbh);
883         for (int i = 0; i < n; i++) {
884             c->drawRect(rects[i], SkPaint{});
885         }
886         sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
887 
888         std::vector<int> results;
889         bbh->search({0,0, 100,100}, &results);
890         REPORTER_ASSERT(r, (int)results.size() == n,
891                         "results.size() == %d, want %d\n", (int)results.size(), n);
892     }
893 }
894 
DEF_TEST(Picture_nested_op_count,r)895 DEF_TEST(Picture_nested_op_count, r) {
896     auto make_pic = [](int n, const sk_sp<SkPicture>& pic) {
897         SkPictureRecorder rec;
898         SkCanvas* c = rec.beginRecording({0,0, 100,100});
899         for (int i = 0; i < n; i++) {
900             if (pic) {
901                 c->drawPicture(pic);
902             } else {
903                 c->drawRect({0,0, 100,100}, SkPaint{});
904             }
905         }
906         return rec.finishRecordingAsPicture();
907     };
908 
909     auto check = [r](const sk_sp<SkPicture>& pic, int shallow, int nested) {
910         int s = pic->approximateOpCount(false);
911         int n = pic->approximateOpCount(true);
912         REPORTER_ASSERT(r, s == shallow);
913         REPORTER_ASSERT(r, n == nested);
914     };
915 
916     sk_sp<SkPicture> leaf1 = make_pic(1, nullptr);
917     check(leaf1, 1, 1);
918 
919     sk_sp<SkPicture> leaf10 = make_pic(10, nullptr);
920     check(leaf10, 10, 10);
921 
922     check(make_pic( 1, leaf1),   1,   1);
923     check(make_pic( 1, leaf10),  1,  10);
924     check(make_pic(10, leaf1),  10,  10);
925     check(make_pic(10, leaf10), 10, 100);
926 }
927