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