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/SkBitmap.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkClipOp.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkDocument.h"
15 #include "include/core/SkFlattenable.h"
16 #include "include/core/SkImageFilter.h"
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/SkPictureRecorder.h"
22 #include "include/core/SkPixmap.h"
23 #include "include/core/SkPoint.h"
24 #include "include/core/SkRect.h"
25 #include "include/core/SkRefCnt.h"
26 #include "include/core/SkRegion.h"
27 #include "include/core/SkSamplingOptions.h"
28 #include "include/core/SkScalar.h"
29 #include "include/core/SkShader.h"
30 #include "include/core/SkSize.h"
31 #include "include/core/SkStream.h"
32 #include "include/core/SkSurface.h"
33 #include "include/core/SkTypes.h"
34 #include "include/core/SkVertices.h"
35 #include "include/effects/SkImageFilters.h"
36 #include "include/private/base/SkTemplates.h"
37 #include "include/private/base/SkMalloc.h"
38 #include "include/utils/SkNWayCanvas.h"
39 #include "include/utils/SkPaintFilterCanvas.h"
40 #include "src/core/SkBigPicture.h"
41 #include "src/core/SkImageFilter_Base.h"
42 #include "src/core/SkRecord.h"
43 #include "src/core/SkRecords.h"
44 #include "src/core/SkSpecialImage.h"
45 #include "src/utils/SkCanvasStack.h"
46 #include "tests/Test.h"
47 
48 #include <cstddef>
49 #include <initializer_list>
50 #include <memory>
51 #include <utility>
52 
53 using namespace skia_private;
54 
55 class SkPicture;
56 class SkReadBuffer;
57 
58 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
59 #include "include/core/SkColorSpace.h"
60 #include "include/private/SkColorData.h"
61 #endif
62 
63 #ifdef SK_SUPPORT_PDF
64 #include "include/docs/SkPDFDocument.h"
65 #endif
66 
67 struct ClipRectVisitor {
68     skiatest::Reporter* r;
69 
70     template <typename T>
operator ()ClipRectVisitor71     SkRect operator()(const T&) {
72         REPORTER_ASSERT(r, false, "unexpected record");
73         return {1,1,0,0};
74     }
75 
operator ()ClipRectVisitor76     SkRect operator()(const SkRecords::ClipRect& op) {
77         return op.rect;
78     }
79 };
80 
DEF_TEST(canvas_unsorted_clip,r)81 DEF_TEST(canvas_unsorted_clip, r) {
82     // Test that sorted and unsorted clip rects are forwarded
83     // to picture subclasses and/or devices sorted.
84     //
85     // We can't just test this with an SkCanvas on stack and
86     // SkCanvas::getLocalClipBounds(), as that only tests the raster device,
87     // which sorts these rects itself.
88     for (SkRect clip : {SkRect{0,0,5,5}, SkRect{5,5,0,0}}) {
89         SkPictureRecorder rec;
90         rec.beginRecording({0,0,10,10})
91             ->clipRect(clip);
92         sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
93 
94         auto bp = (const SkBigPicture*)pic.get();
95         const SkRecord* record = bp->record();
96 
97         REPORTER_ASSERT(r, record->count() == 1);
98         REPORTER_ASSERT(r, record->visit(0, ClipRectVisitor{r})
99                                 .isSorted());
100     }
101 }
102 
DEF_TEST(canvas_clipbounds,reporter)103 DEF_TEST(canvas_clipbounds, reporter) {
104     SkCanvas canvas(10, 10);
105     SkIRect irect, irect2;
106     SkRect rect, rect2;
107 
108     irect = canvas.getDeviceClipBounds();
109     REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
110     REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
111     REPORTER_ASSERT(reporter, irect == irect2);
112 
113     // local bounds are always too big today -- can we trim them?
114     rect = canvas.getLocalClipBounds();
115     REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10)));
116     REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
117     REPORTER_ASSERT(reporter, rect == rect2);
118 
119     canvas.clipRect(SkRect::MakeEmpty());
120 
121     irect = canvas.getDeviceClipBounds();
122     REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
123     REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
124     REPORTER_ASSERT(reporter, irect == irect2);
125 
126     rect = canvas.getLocalClipBounds();
127     REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
128     REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
129     REPORTER_ASSERT(reporter, rect == rect2);
130 
131     // Test for wacky sizes that we (historically) have guarded against
132     {
133         SkCanvas c(-10, -20);
134         REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty());
135 
136         SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
137     }
138 }
139 
140 #ifdef SK_SUPPORT_PDF
141 
142 // Will call proc with multiple styles of canvas (recording, raster, pdf)
multi_canvas_driver(int w,int h,F proc)143 template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
144     proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
145 
146     SkNullWStream stream;
147     if (auto doc = SkPDF::MakeDocument(&stream)) {
148         proc(doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
149     }
150 
151     proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
152 }
153 
154 const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
155 
test_restriction(skiatest::Reporter * reporter,SkCanvas * canvas)156 static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
157     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);
158 
159     const SkIRect restrictionR = { 2, 2, 8, 8 };
160     canvas->androidFramework_setDeviceClipRestriction(restrictionR);
161     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);
162 
163     const SkIRect clipR = { 4, 4, 6, 6 };
164     canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
165     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
166 }
167 
168 /**
169  *  Clip restriction logic exists in the canvas itself, and in various kinds of devices.
170  *
171  *  This test explicitly tries to exercise that variety:
172  *  - picture : empty device but exercises canvas itself
173  *  - pdf : uses SkClipStack in its device (as does SVG and GPU)
174  *  - raster : uses SkRasterClip in its device
175  */
DEF_TEST(canvas_clip_restriction,reporter)176 DEF_TEST(canvas_clip_restriction, reporter) {
177     multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
178                         [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
179 }
180 
DEF_TEST(canvas_empty_clip,reporter)181 DEF_TEST(canvas_empty_clip, reporter) {
182     multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
183         canvas->save();
184         canvas->clipRect({0, 0, 20, 40 });
185         REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
186         canvas->clipRect({30, 0, 50, 40 });
187         REPORTER_ASSERT(reporter, canvas->isClipEmpty());
188     });
189 }
190 
191 #endif // SK_SUPPORT_PDF
192 
DEF_TEST(CanvasNewRasterTest,reporter)193 DEF_TEST(CanvasNewRasterTest, reporter) {
194     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
195     const size_t minRowBytes = info.minRowBytes();
196     const size_t size = info.computeByteSize(minRowBytes);
197     AutoTMalloc<SkPMColor> storage(size);
198     SkPMColor* baseAddr = storage.get();
199     sk_bzero(baseAddr, size);
200 
201     std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
202     REPORTER_ASSERT(reporter, canvas);
203 
204     SkPixmap pmap;
205     const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
206     REPORTER_ASSERT(reporter, addr);
207     REPORTER_ASSERT(reporter, info == pmap.info());
208     REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
209     for (int y = 0; y < info.height(); ++y) {
210         for (int x = 0; x < info.width(); ++x) {
211             REPORTER_ASSERT(reporter, 0 == addr[x]);
212         }
213         addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
214     }
215 
216     // unaligned rowBytes
217     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr,
218                                                                     minRowBytes + 1));
219 
220     // now try a deliberately bad info
221     info = info.makeWH(-1, info.height());
222     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
223 
224     // too big
225     info = info.makeWH(1 << 30, 1 << 30);
226     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
227 
228     // not a valid pixel type
229     info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
230     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
231 
232     // We should not succeed with a zero-sized valid info
233     info = SkImageInfo::MakeN32Premul(0, 0);
234     canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
235     REPORTER_ASSERT(reporter, nullptr == canvas);
236 }
237 
make_path_from_rect(SkRect r)238 static SkPath make_path_from_rect(SkRect r) {
239     SkPath path;
240     path.addRect(r);
241     return path;
242 }
243 
make_region_from_irect(SkIRect r)244 static SkRegion make_region_from_irect(SkIRect r) {
245     SkRegion region;
246     region.setRect(r);
247     return region;
248 }
249 
make_n32_bitmap(int w,int h,SkColor c=SK_ColorWHITE)250 static SkBitmap make_n32_bitmap(int w, int h, SkColor c = SK_ColorWHITE) {
251     SkBitmap bm;
252     bm.allocN32Pixels(w, h);
253     bm.eraseColor(c);
254     return bm;
255 }
256 
257 // Constants used by test steps
258 static constexpr SkRect kRect = {0, 0, 2, 1};
259 static constexpr SkColor kColor = 0x01020304;
260 static constexpr int kWidth = 2;
261 static constexpr int kHeight = 2;
262 
263 using CanvasTest = void (*)(SkCanvas*, skiatest::Reporter*);
264 
265 static CanvasTest kCanvasTests[] = {
__anon411bb63c0302() 266     [](SkCanvas* c, skiatest::Reporter* r) {
267         c->translate(SkIntToScalar(1), SkIntToScalar(2));
268     },
__anon411bb63c0402() 269     [](SkCanvas* c, skiatest::Reporter* r) {
270         c->scale(SkIntToScalar(1), SkIntToScalar(2));
271     },
__anon411bb63c0502() 272     [](SkCanvas* c, skiatest::Reporter* r) {
273         c->rotate(SkIntToScalar(1));
274     },
__anon411bb63c0602() 275     [](SkCanvas* c, skiatest::Reporter* r) {
276         c->skew(SkIntToScalar(1), SkIntToScalar(2));
277     },
__anon411bb63c0702() 278     [](SkCanvas* c, skiatest::Reporter* r) {
279         c->concat(SkMatrix::Scale(2, 3));
280     },
__anon411bb63c0802() 281     [](SkCanvas* c, skiatest::Reporter* r) {
282         c->setMatrix(SkMatrix::Scale(2, 3));
283     },
__anon411bb63c0902() 284     [](SkCanvas* c, skiatest::Reporter* r) {
285         c->clipRect(kRect);
286     },
__anon411bb63c0a02() 287     [](SkCanvas* c, skiatest::Reporter* r) {
288         c->clipPath(make_path_from_rect(SkRect{0, 0, 2, 1}));
289     },
__anon411bb63c0b02() 290     [](SkCanvas* c, skiatest::Reporter* r) {
291         c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
292     },
__anon411bb63c0c02() 293     [](SkCanvas* c, skiatest::Reporter* r) {
294         c->clear(kColor);
295     },
__anon411bb63c0d02() 296     [](SkCanvas* c, skiatest::Reporter* r) {
297         int saveCount = c->getSaveCount();
298         c->save();
299         c->translate(SkIntToScalar(1), SkIntToScalar(2));
300         c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
301         c->restore();
302         REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
303         REPORTER_ASSERT(r, c->getTotalMatrix().isIdentity());
304         //REPORTER_ASSERT(reporter, c->getTotalClip() != kTestRegion);
305     },
__anon411bb63c0e02() 306     [](SkCanvas* c, skiatest::Reporter* r) {
307         int saveCount = c->getSaveCount();
308         c->saveLayer(nullptr, nullptr);
309         c->restore();
310         REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
311     },
__anon411bb63c0f02() 312     [](SkCanvas* c, skiatest::Reporter* r) {
313         int saveCount = c->getSaveCount();
314         c->saveLayer(&kRect, nullptr);
315         c->restore();
316         REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
317     },
__anon411bb63c1002() 318     [](SkCanvas* c, skiatest::Reporter* r) {
319         int saveCount = c->getSaveCount();
320         SkPaint p;
321         c->saveLayer(nullptr, &p);
322         c->restore();
323         REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
324     },
__anon411bb63c1102() 325     [](SkCanvas* c, skiatest::Reporter* r) {
326         // This test exercises a functionality in SkPicture that leads to the
327         // recording of restore offset placeholders.  This test will trigger an
328         // assertion at playback time if the placeholders are not properly
329         // filled when the recording ends.
330         c->clipRect(kRect);
331         c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
332     },
__anon411bb63c1202() 333     [](SkCanvas* c, skiatest::Reporter* r) {
334         // exercise fix for http://code.google.com/p/skia/issues/detail?id=560
335         // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
336         SkPaint paint;
337         paint.setStrokeWidth(SkIntToScalar(1));
338         paint.setStyle(SkPaint::kStroke_Style);
339         SkPath path;
340         path.moveTo(SkPoint{ 0, 0 });
341         path.lineTo(SkPoint{ 0, SK_ScalarNearlyZero });
342         path.lineTo(SkPoint{ SkIntToScalar(1), 0 });
343         path.lineTo(SkPoint{ SkIntToScalar(1), SK_ScalarNearlyZero/2 });
344         // test nearly zero length path
345         c->drawPath(path, paint);
346     },
__anon411bb63c1302() 347     [](SkCanvas* c, skiatest::Reporter* r) {
348         SkPictureRecorder recorder;
349         SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth),
350                                                        SkIntToScalar(kHeight));
351         testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
352         testCanvas->clipRect(kRect);
353         testCanvas->drawRect(kRect, SkPaint());
354         c->drawPicture(recorder.finishRecordingAsPicture());
355     },
__anon411bb63c1402() 356     [](SkCanvas* c, skiatest::Reporter* r) {
357         int baseSaveCount = c->getSaveCount();
358         int n = c->save();
359         REPORTER_ASSERT(r, baseSaveCount == n);
360         REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
361         c->save();
362         c->save();
363         REPORTER_ASSERT(r, baseSaveCount + 3 == c->getSaveCount());
364         c->restoreToCount(baseSaveCount + 1);
365         REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
366 
367        // should this pin to 1, or be a no-op, or crash?
368        c->restoreToCount(0);
369        REPORTER_ASSERT(r, 1 == c->getSaveCount());
370     },
__anon411bb63c1502() 371     [](SkCanvas* c, skiatest::Reporter* r) {
372        // This test step challenges the TestDeferredCanvasStateConsistency
373        // test cases because the opaque paint can trigger an optimization
374        // that discards previously recorded commands. The challenge is to maintain
375        // correct clip and matrix stack state.
376        c->resetMatrix();
377        c->rotate(SkIntToScalar(30));
378        c->save();
379        c->translate(SkIntToScalar(2), SkIntToScalar(1));
380        c->save();
381        c->scale(SkIntToScalar(3), SkIntToScalar(3));
382        SkPaint paint;
383        paint.setColor(0xFFFFFFFF);
384        c->drawPaint(paint);
385        c->restore();
386        c->restore();
387     },
__anon411bb63c1602() 388     [](SkCanvas* c, skiatest::Reporter* r) {
389        // This test step challenges the TestDeferredCanvasStateConsistency
390        // test case because the canvas flush on a deferred canvas will
391        // reset the recording session. The challenge is to maintain correct
392        // clip and matrix stack state on the playback canvas.
393        c->resetMatrix();
394        c->rotate(SkIntToScalar(30));
395        c->save();
396        c->translate(SkIntToScalar(2), SkIntToScalar(1));
397        c->save();
398        c->scale(SkIntToScalar(3), SkIntToScalar(3));
399        c->drawRect(kRect, SkPaint());
400        c->flush();
401        c->restore();
402        c->restore();
403     },
__anon411bb63c1702() 404     [](SkCanvas* c, skiatest::Reporter* r) {
405         SkPoint pts[4];
406         pts[0].set(0, 0);
407         pts[1].set(SkIntToScalar(kWidth), 0);
408         pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
409         pts[3].set(0, SkIntToScalar(kHeight));
410         SkPaint paint;
411         SkBitmap bitmap(make_n32_bitmap(kWidth, kHeight, 0x05060708));
412         paint.setShader(bitmap.makeShader(SkSamplingOptions()));
413         c->drawVertices(
414             SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, nullptr),
415             SkBlendMode::kModulate, paint);
416     }
417 };
418 
DEF_TEST(Canvas_bitmap,reporter)419 DEF_TEST(Canvas_bitmap, reporter) {
420     for (const CanvasTest& test : kCanvasTests) {
421         SkBitmap referenceStore = make_n32_bitmap(kWidth, kHeight);
422         SkCanvas referenceCanvas(referenceStore);
423         test(&referenceCanvas, reporter);
424     }
425 }
426 
427 #ifdef SK_SUPPORT_PDF
DEF_TEST(Canvas_pdf,reporter)428 DEF_TEST(Canvas_pdf, reporter) {
429     for (const CanvasTest& test : kCanvasTests) {
430         SkNullWStream outStream;
431         if (auto doc = SkPDF::MakeDocument(&outStream)) {
432             SkCanvas* canvas = doc->beginPage(SkIntToScalar(kWidth),
433                                               SkIntToScalar(kHeight));
434             REPORTER_ASSERT(reporter, canvas);
435             test(canvas, reporter);
436         }
437     }
438 }
439 #endif
440 
DEF_TEST(Canvas_SaveState,reporter)441 DEF_TEST(Canvas_SaveState, reporter) {
442     SkCanvas canvas(10, 10);
443     REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
444 
445     int n = canvas.save();
446     REPORTER_ASSERT(reporter, 1 == n);
447     REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
448 
449     n = canvas.saveLayer(nullptr, nullptr);
450     REPORTER_ASSERT(reporter, 2 == n);
451     REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
452 
453     canvas.restore();
454     REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
455     canvas.restore();
456     REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
457 }
458 
DEF_TEST(Canvas_ClipEmptyPath,reporter)459 DEF_TEST(Canvas_ClipEmptyPath, reporter) {
460     SkCanvas canvas(10, 10);
461     canvas.save();
462     SkPath path;
463     canvas.clipPath(path);
464     canvas.restore();
465     canvas.save();
466     path.moveTo(5, 5);
467     canvas.clipPath(path);
468     canvas.restore();
469     canvas.save();
470     path.moveTo(7, 7);
471     canvas.clipPath(path);  // should not assert here
472     canvas.restore();
473 }
474 
475 namespace {
476 
477 class MockFilterCanvas : public SkPaintFilterCanvas {
478 public:
MockFilterCanvas(SkCanvas * canvas)479     MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
480 
481 protected:
onFilter(SkPaint &) const482     bool onFilter(SkPaint&) const override { return true; }
483 
484 private:
485     using INHERITED = SkPaintFilterCanvas;
486 };
487 
488 } // anonymous namespace
489 
490 // SkPaintFilterCanvas should inherit the initial target canvas state.
DEF_TEST(PaintFilterCanvas_ConsistentState,reporter)491 DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) {
492     SkCanvas canvas(100, 100);
493     canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75));
494     canvas.scale(0.5f, 0.75f);
495 
496     MockFilterCanvas filterCanvas(&canvas);
497     REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
498     REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
499 
500     filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100));
501     filterCanvas.scale(0.75f, 0.5f);
502     REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
503     REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
504 }
505 
506 ///////////////////////////////////////////////////////////////////////////////////////////////////
507 
508 namespace {
509 
510 // Subclass that takes a bool*, which it updates in its construct (true) and destructor (false)
511 // to allow the caller to know how long the object is alive.
512 class LifeLineCanvas : public SkCanvas {
513     bool*   fLifeLine;
514 public:
LifeLineCanvas(int w,int h,bool * lifeline)515     LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
516         *fLifeLine = true;
517     }
~LifeLineCanvas()518     ~LifeLineCanvas() override {
519         *fLifeLine = false;
520     }
521 };
522 
523 }  // namespace
524 
525 // Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
DEF_TEST(NWayCanvas,r)526 DEF_TEST(NWayCanvas, r) {
527     const int w = 10;
528     const int h = 10;
529     bool life[2];
530     {
531         LifeLineCanvas c0(w, h, &life[0]);
532         REPORTER_ASSERT(r, life[0]);
533     }
534     REPORTER_ASSERT(r, !life[0]);
535 
536 
537     std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
538     std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
539     REPORTER_ASSERT(r, life[0]);
540     REPORTER_ASSERT(r, life[1]);
541 
542     {
543         SkNWayCanvas nway(w, h);
544         nway.addCanvas(c0.get());
545         nway.addCanvas(c1.get());
546         REPORTER_ASSERT(r, life[0]);
547         REPORTER_ASSERT(r, life[1]);
548     }
549     // Now assert that the death of the nway has NOT also killed the sub-canvases
550     REPORTER_ASSERT(r, life[0]);
551     REPORTER_ASSERT(r, life[1]);
552 }
553 
554 // Check that CanvasStack DOES manage the lifetime of its sub-canvases
DEF_TEST(CanvasStack,r)555 DEF_TEST(CanvasStack, r) {
556     const int w = 10;
557     const int h = 10;
558     bool life[2];
559     std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
560     std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
561     REPORTER_ASSERT(r, life[0]);
562     REPORTER_ASSERT(r, life[1]);
563 
564     {
565         SkCanvasStack stack(w, h);
566         stack.pushCanvas(std::move(c0), {0,0});
567         stack.pushCanvas(std::move(c1), {0,0});
568         REPORTER_ASSERT(r, life[0]);
569         REPORTER_ASSERT(r, life[1]);
570     }
571     // Now assert that the death of the canvasstack has also killed the sub-canvases
572     REPORTER_ASSERT(r, !life[0]);
573     REPORTER_ASSERT(r, !life[1]);
574 }
575 
test_cliptype(SkCanvas * canvas,skiatest::Reporter * r)576 static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
577     REPORTER_ASSERT(r, !canvas->isClipEmpty());
578     REPORTER_ASSERT(r, canvas->isClipRect());
579 
580     canvas->save();
581     canvas->clipRect({0, 0, 0, 0});
582     REPORTER_ASSERT(r, canvas->isClipEmpty());
583     REPORTER_ASSERT(r, !canvas->isClipRect());
584     canvas->restore();
585 
586     canvas->save();
587     canvas->clipRect({2, 2, 6, 6});
588     REPORTER_ASSERT(r, !canvas->isClipEmpty());
589     REPORTER_ASSERT(r, canvas->isClipRect());
590     canvas->restore();
591 
592     canvas->save();
593     canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference);  // punch a hole in the clip
594     REPORTER_ASSERT(r, !canvas->isClipEmpty());
595     REPORTER_ASSERT(r, !canvas->isClipRect());
596     canvas->restore();
597 
598     REPORTER_ASSERT(r, !canvas->isClipEmpty());
599     REPORTER_ASSERT(r, canvas->isClipRect());
600 }
601 
DEF_TEST(CanvasClipType,r)602 DEF_TEST(CanvasClipType, r) {
603     // test rasterclip backend
604     test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
605 
606 #ifdef SK_SUPPORT_PDF
607     // test clipstack backend
608     SkDynamicMemoryWStream stream;
609     if (auto doc = SkPDF::MakeDocument(&stream)) {
610         test_cliptype(doc->beginPage(100, 100), r);
611     }
612 #endif
613 }
614 
615 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
DEF_TEST(Canvas_LegacyColorBehavior,r)616 DEF_TEST(Canvas_LegacyColorBehavior, r) {
617     sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
618                                                    SkNamedGamut::kAdobeRGB);
619 
620     // Make a Adobe RGB bitmap.
621     SkBitmap bitmap;
622     bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs));
623     bitmap.eraseColor(0xFF000000);
624 
625     // Wrap it in a legacy canvas.  Test that the canvas behaves like a legacy canvas.
626     SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy);
627     REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace());
628     SkPaint p;
629     p.setColor(SK_ColorRED);
630     canvas.drawIRect(SkIRect::MakeWH(1, 1), p);
631     REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0)));
632 }
633 #endif
634 
635 namespace {
636 
637 class ZeroBoundsImageFilter : public SkImageFilter_Base {
638 public:
Make()639     static sk_sp<SkImageFilter> Make() { return sk_sp<SkImageFilter>(new ZeroBoundsImageFilter); }
640 
641 protected:
onFilterImage(const Context &,SkIPoint *) const642     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint*) const override {
643         return nullptr;
644     }
onFilterNodeBounds(const SkIRect &,const SkMatrix &,MapDirection,const SkIRect * inputRect) const645     SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&,
646                                MapDirection, const SkIRect* inputRect) const override {
647         return SkIRect::MakeEmpty();
648     }
649 
650 private:
651     SK_FLATTENABLE_HOOKS(ZeroBoundsImageFilter)
652 
ZeroBoundsImageFilter()653     ZeroBoundsImageFilter() : INHERITED(nullptr, 0, nullptr) {}
654 
655     using INHERITED = SkImageFilter_Base;
656 };
657 
CreateProc(SkReadBuffer & buffer)658 sk_sp<SkFlattenable> ZeroBoundsImageFilter::CreateProc(SkReadBuffer& buffer) {
659     SkDEBUGFAIL("Should never get here");
660     return nullptr;
661 }
662 
663 }  // anonymous namespace
664 
DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter,r)665 DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter, r) {
666     SkCanvas canvas(10, 10);
667     SkPaint p;
668     p.setImageFilter(ZeroBoundsImageFilter::Make());
669     // This should not fail any assert.
670     canvas.saveLayer(nullptr, &p);
671     REPORTER_ASSERT(r, canvas.getDeviceClipBounds().isEmpty());
672     canvas.restore();
673 }
674 
675 // Test that we don't crash/assert when building a canvas with degenerate coordintes
676 // (esp. big ones, that might invoke tiling).
DEF_TEST(Canvas_degenerate_dimension,reporter)677 DEF_TEST(Canvas_degenerate_dimension, reporter) {
678     // Need a paint that will sneak us past the quickReject in SkCanvas, so we can test the
679     // raster code further downstream.
680     SkPaint paint;
681     paint.setImageFilter(SkImageFilters::Shader(SkShaders::Color(SK_ColorBLACK), nullptr));
682     REPORTER_ASSERT(reporter, !paint.canComputeFastBounds());
683 
684     const int big = 100 * 1024; // big enough to definitely trigger tiling
685     const SkISize sizes[] {SkISize{0, big}, {big, 0}, {0, 0}};
686     for (SkISize size : sizes) {
687         SkBitmap bm;
688         bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height()));
689         SkCanvas canvas(bm);
690         canvas.drawRect({0, 0, 100, 90*1024}, paint);
691     }
692 }
693 
DEF_TEST(Canvas_ClippedOutImageFilter,reporter)694 DEF_TEST(Canvas_ClippedOutImageFilter, reporter) {
695     SkCanvas canvas(100, 100);
696 
697     SkPaint p;
698     p.setColor(SK_ColorGREEN);
699     p.setImageFilter(SkImageFilters::Blur(3.0f, 3.0f, nullptr, nullptr));
700 
701     SkRect blurredRect = SkRect::MakeXYWH(60, 10, 30, 30);
702 
703     SkMatrix invM;
704     invM.setRotate(-45);
705     invM.mapRect(&blurredRect);
706 
707     const SkRect clipRect = SkRect::MakeXYWH(0, 50, 50, 50);
708 
709     canvas.clipRect(clipRect);
710 
711     canvas.rotate(45);
712     const SkMatrix preCTM = canvas.getTotalMatrix();
713     canvas.drawRect(blurredRect, p);
714     const SkMatrix postCTM = canvas.getTotalMatrix();
715     REPORTER_ASSERT(reporter, preCTM == postCTM);
716 }
717 
DEF_TEST(canvas_savelayer_destructor,reporter)718 DEF_TEST(canvas_savelayer_destructor, reporter) {
719     // What should happen in our destructor if we have unbalanced saveLayers?
720 
721     SkPMColor pixels[16];
722     const SkImageInfo info = SkImageInfo::MakeN32Premul(4, 4);
723     SkPixmap pm(info, pixels, 4 * sizeof(SkPMColor));
724 
725     // check all of the pixel values in pm
726     auto check_pixels = [&](SkColor expected) {
727         const SkPMColor pmc = SkPreMultiplyColor(expected);
728         for (int y = 0; y < pm.info().height(); ++y) {
729             for (int x = 0; x < pm.info().width(); ++x) {
730                 if (*pm.addr32(x, y) != pmc) {
731                     ERRORF(reporter, "check_pixels_failed");
732                     return;
733                 }
734             }
735         }
736     };
737 
738     auto do_test = [&](int saveCount, int restoreCount) {
739         SkASSERT(restoreCount <= saveCount);
740 
741         auto surf = SkSurface::MakeRasterDirect(pm);
742         auto canvas = surf->getCanvas();
743 
744         canvas->clear(SK_ColorRED);
745         check_pixels(SK_ColorRED);
746 
747         for (int i = 0; i < saveCount; ++i) {
748             canvas->saveLayer(nullptr, nullptr);
749         }
750 
751         canvas->clear(SK_ColorBLUE);
752         // so far, we still expect to see the red, since the blue was drawn in a layer
753         check_pixels(SK_ColorRED);
754 
755         for (int i = 0; i < restoreCount; ++i) {
756             canvas->restore();
757         }
758         // by returning, we are implicitly deleting the surface, and its associated canvas
759     };
760 
761     do_test(1, 1);
762     // since we called restore, we expect to see now see blue
763     check_pixels(SK_ColorBLUE);
764 
765     // Now repeat that, but delete the canvas before we restore it
766     do_test(1, 0);
767     // We don't blit the unbalanced saveLayers, so we expect to see red (not the layer's blue)
768     check_pixels(SK_ColorRED);
769 
770     // Finally, test with multiple unbalanced saveLayers. This led to a crash in an earlier
771     // implementation (crbug.com/1238731)
772     do_test(2, 0);
773     check_pixels(SK_ColorRED);
774 }
775