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