blob: 65ad3431fd632c98e759bc6add82ba4e560b9e9f [file] [log] [blame]
reed@google.com37f3ae02011-11-28 16:06:04 +00001/*
junov@chromium.org1cc8f6f2012-02-22 21:00:42 +00002 * Copyright 2012 Google Inc.
reed@google.com37f3ae02011-11-28 16:06:04 +00003 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
junov@chromium.org1cc8f6f2012-02-22 21:00:42 +00007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Ben Wagnercbf6d302019-05-06 15:13:30 -040013#include "include/core/SkDocument.h"
14#include "include/core/SkFlattenable.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#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/docs/SkPDFDocument.h"
Michael Ludwig55edb502019-08-05 10:41:10 -040035#include "include/effects/SkImageFilters.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "include/private/SkMalloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "include/private/SkTemplates.h"
38#include "include/utils/SkNWayCanvas.h"
39#include "include/utils/SkPaintFilterCanvas.h"
Mike Klein3c685bf2020-09-15 13:10:52 -050040#include "src/core/SkBigPicture.h"
Michael Ludwig8ee6cf32019-08-02 09:57:04 -040041#include "src/core/SkImageFilter_Base.h"
Mike Klein3c685bf2020-09-15 13:10:52 -050042#include "src/core/SkRecord.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050043#include "src/core/SkSpecialImage.h"
44#include "src/utils/SkCanvasStack.h"
45#include "tests/Test.h"
reed@google.com37f3ae02011-11-28 16:06:04 +000046
Ben Wagnerb607a8f2018-03-12 13:46:21 -040047#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
Mike Kleinc0bd9f92019-04-23 12:05:21 -050048#include "include/core/SkColorSpace.h"
49#include "include/private/SkColorData.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040050#endif
51
52#include <memory>
53#include <utility>
54
Ben Wagnerb607a8f2018-03-12 13:46:21 -040055class SkReadBuffer;
Ben Wagnerb607a8f2018-03-12 13:46:21 -040056
Mike Klein3c685bf2020-09-15 13:10:52 -050057struct ClipRectVisitor {
58 skiatest::Reporter* r;
59
60 template <typename T>
61 SkRect operator()(const T&) {
62 REPORTER_ASSERT(r, false, "unexpected record");
63 return {1,1,0,0};
64 }
65
66 SkRect operator()(const SkRecords::ClipRect& op) {
67 return op.rect;
68 }
69};
70
71DEF_TEST(canvas_unsorted_clip, r) {
72 // Test that sorted and unsorted clip rects are forwarded
73 // to picture subclasses and/or devices sorted.
74 //
75 // We can't just test this with an SkCanvas on stack and
76 // SkCanvas::getLocalClipBounds(), as that only tests the raster device,
77 // which sorts these rects itself.
78 for (SkRect clip : {SkRect{0,0,5,5}, SkRect{5,5,0,0}}) {
79 SkPictureRecorder rec;
80 rec.beginRecording({0,0,10,10})
81 ->clipRect(clip);
82 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture();
83
84 auto bp = (const SkBigPicture*)pic.get();
85 const SkRecord* record = bp->record();
86
87 REPORTER_ASSERT(r, record->count() == 1);
88 REPORTER_ASSERT(r, record->visit(0, ClipRectVisitor{r})
89 .isSorted());
90 }
91}
92
Mike Reed918e1442017-01-23 11:39:45 -050093DEF_TEST(canvas_clipbounds, reporter) {
94 SkCanvas canvas(10, 10);
Mike Reed6f4a9b22017-01-24 09:13:40 -050095 SkIRect irect, irect2;
96 SkRect rect, rect2;
Mike Reed918e1442017-01-23 11:39:45 -050097
98 irect = canvas.getDeviceClipBounds();
99 REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
Mike Reed6f4a9b22017-01-24 09:13:40 -0500100 REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
101 REPORTER_ASSERT(reporter, irect == irect2);
102
Mike Reed918e1442017-01-23 11:39:45 -0500103 // local bounds are always too big today -- can we trim them?
104 rect = canvas.getLocalClipBounds();
105 REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10)));
Mike Reed6f4a9b22017-01-24 09:13:40 -0500106 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
107 REPORTER_ASSERT(reporter, rect == rect2);
Mike Reed918e1442017-01-23 11:39:45 -0500108
109 canvas.clipRect(SkRect::MakeEmpty());
110
111 irect = canvas.getDeviceClipBounds();
112 REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
Mike Reed6f4a9b22017-01-24 09:13:40 -0500113 REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
114 REPORTER_ASSERT(reporter, irect == irect2);
115
Mike Reed918e1442017-01-23 11:39:45 -0500116 rect = canvas.getLocalClipBounds();
117 REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
Mike Reed6f4a9b22017-01-24 09:13:40 -0500118 REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
119 REPORTER_ASSERT(reporter, rect == rect2);
Mike Reed566e53c2017-03-10 10:49:45 -0500120
121 // Test for wacky sizes that we (historically) have guarded against
122 {
123 SkCanvas c(-10, -20);
124 REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty());
Mike Reeddc9f0db2017-03-10 13:54:16 -0500125
126 SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
Mike Reed566e53c2017-03-10 10:49:45 -0500127 }
Mike Reed918e1442017-01-23 11:39:45 -0500128}
129
Robert Phillips09dfc472017-09-13 15:25:47 -0400130// Will call proc with multiple styles of canvas (recording, raster, pdf)
Mike Reed02be3c12017-03-23 12:34:15 -0400131template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
132 proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
133
134 SkNullWStream stream;
Hal Canary23564b92018-09-07 14:33:14 -0400135 if (auto doc = SkPDF::MakeDocument(&stream)) {
Hal Canary4125b612018-03-20 14:17:00 -0400136 proc(doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
137 }
Mike Reed02be3c12017-03-23 12:34:15 -0400138
139 proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
140}
141
Mike Reed2a65cc02017-03-22 10:01:53 -0400142const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
143
144static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
145 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);
146
147 const SkIRect restrictionR = { 2, 2, 8, 8 };
148 canvas->androidFramework_setDeviceClipRestriction(restrictionR);
149 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);
150
151 const SkIRect clipR = { 4, 4, 6, 6 };
152 canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
153 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
Mike Reed2a65cc02017-03-22 10:01:53 -0400154}
155
156/**
157 * Clip restriction logic exists in the canvas itself, and in various kinds of devices.
158 *
159 * This test explicitly tries to exercise that variety:
160 * - picture : empty device but exercises canvas itself
161 * - pdf : uses SkClipStack in its device (as does SVG and GPU)
162 * - raster : uses SkRasterClip in its device
163 */
164DEF_TEST(canvas_clip_restriction, reporter) {
Mike Reed02be3c12017-03-23 12:34:15 -0400165 multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
166 [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
167}
Mike Reed2a65cc02017-03-22 10:01:53 -0400168
Mike Reed02be3c12017-03-23 12:34:15 -0400169DEF_TEST(canvas_empty_clip, reporter) {
170 multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
171 canvas->save();
172 canvas->clipRect({0, 0, 20, 40 });
173 REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
174 canvas->clipRect({30, 0, 50, 40 });
175 REPORTER_ASSERT(reporter, canvas->isClipEmpty());
176 });
Mike Reed2a65cc02017-03-22 10:01:53 -0400177}
178
Ben Wagnercbf6d302019-05-06 15:13:30 -0400179DEF_TEST(CanvasNewRasterTest, reporter) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000180 SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
reed3054be12014-12-10 07:24:28 -0800181 const size_t minRowBytes = info.minRowBytes();
Mike Reedf0ffb892017-10-03 14:47:21 -0400182 const size_t size = info.computeByteSize(minRowBytes);
scroggo565901d2015-12-10 10:44:13 -0800183 SkAutoTMalloc<SkPMColor> storage(size);
184 SkPMColor* baseAddr = storage.get();
reed3054be12014-12-10 07:24:28 -0800185 sk_bzero(baseAddr, size);
186
Mike Reed5df49342016-11-12 08:06:55 -0600187 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000188 REPORTER_ASSERT(reporter, canvas);
189
reed6ceeebd2016-03-09 14:26:26 -0800190 SkPixmap pmap;
191 const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000192 REPORTER_ASSERT(reporter, addr);
reed6ceeebd2016-03-09 14:26:26 -0800193 REPORTER_ASSERT(reporter, info == pmap.info());
194 REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000195 for (int y = 0; y < info.height(); ++y) {
196 for (int x = 0; x < info.width(); ++x) {
197 REPORTER_ASSERT(reporter, 0 == addr[x]);
198 }
reed6ceeebd2016-03-09 14:26:26 -0800199 addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000200 }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000201
Leon Scroggins III3eedc972020-01-22 10:44:00 -0500202 // unaligned rowBytes
203 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr,
204 minRowBytes + 1));
205
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000206 // now try a deliberately bad info
reede5ea5002014-09-03 11:54:58 -0700207 info = info.makeWH(-1, info.height());
Mike Reed5df49342016-11-12 08:06:55 -0600208 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000209
210 // too big
reede5ea5002014-09-03 11:54:58 -0700211 info = info.makeWH(1 << 30, 1 << 30);
Mike Reed5df49342016-11-12 08:06:55 -0600212 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
skia.committer@gmail.com0e530752014-02-28 03:02:05 +0000213
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000214 // not a valid pixel type
reede5ea5002014-09-03 11:54:58 -0700215 info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
Mike Reed5df49342016-11-12 08:06:55 -0600216 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000217
Brian Osman431ac562018-10-10 13:20:38 -0400218 // We should not succeed with a zero-sized valid info
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000219 info = SkImageInfo::MakeN32Premul(0, 0);
Mike Reed5df49342016-11-12 08:06:55 -0600220 canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
Brian Osman431ac562018-10-10 13:20:38 -0400221 REPORTER_ASSERT(reporter, nullptr == canvas);
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +0000222}
223
Hal Canary5f2807b2019-05-06 16:51:06 -0400224static SkPath make_path_from_rect(SkRect r) {
225 SkPath path;
226 path.addRect(r);
227 return path;
228}
reed@google.com37f3ae02011-11-28 16:06:04 +0000229
Hal Canary5f2807b2019-05-06 16:51:06 -0400230static SkRegion make_region_from_irect(SkIRect r) {
231 SkRegion region;
232 region.setRect(r);
233 return region;
234}
Ben Wagnercbf6d302019-05-06 15:13:30 -0400235
Hal Canary5f2807b2019-05-06 16:51:06 -0400236static SkBitmap make_n32_bitmap(int w, int h, SkColor c = SK_ColorWHITE) {
237 SkBitmap bm;
238 bm.allocN32Pixels(w, h);
239 bm.eraseColor(c);
240 return bm;
241}
Ben Wagnercbf6d302019-05-06 15:13:30 -0400242
Hal Canary5f2807b2019-05-06 16:51:06 -0400243// Constants used by test steps
244static constexpr SkRect kRect = {0, 0, 2, 1};
245static constexpr SkColor kColor = 0x01020304;
246static constexpr int kWidth = 2;
247static constexpr int kHeight = 2;
Ben Wagnercbf6d302019-05-06 15:13:30 -0400248
Hal Canary5f2807b2019-05-06 16:51:06 -0400249using CanvasTest = void (*)(SkCanvas*, skiatest::Reporter*);
Ben Wagnercbf6d302019-05-06 15:13:30 -0400250
Hal Canary5f2807b2019-05-06 16:51:06 -0400251static CanvasTest kCanvasTests[] = {
252 [](SkCanvas* c, skiatest::Reporter* r) {
253 c->translate(SkIntToScalar(1), SkIntToScalar(2));
254 },
255 [](SkCanvas* c, skiatest::Reporter* r) {
256 c->scale(SkIntToScalar(1), SkIntToScalar(2));
257 },
258 [](SkCanvas* c, skiatest::Reporter* r) {
259 c->rotate(SkIntToScalar(1));
260 },
261 [](SkCanvas* c, skiatest::Reporter* r) {
262 c->skew(SkIntToScalar(1), SkIntToScalar(2));
263 },
264 [](SkCanvas* c, skiatest::Reporter* r) {
Mike Reed1f607332020-05-21 12:11:27 -0400265 c->concat(SkMatrix::Scale(2, 3));
Hal Canary5f2807b2019-05-06 16:51:06 -0400266 },
267 [](SkCanvas* c, skiatest::Reporter* r) {
Mike Reed1f607332020-05-21 12:11:27 -0400268 c->setMatrix(SkMatrix::Scale(2, 3));
Hal Canary5f2807b2019-05-06 16:51:06 -0400269 },
270 [](SkCanvas* c, skiatest::Reporter* r) {
271 c->clipRect(kRect);
272 },
273 [](SkCanvas* c, skiatest::Reporter* r) {
274 c->clipPath(make_path_from_rect(SkRect{0, 0, 2, 1}));
275 },
276 [](SkCanvas* c, skiatest::Reporter* r) {
Michael Ludwige4b79692020-09-16 13:55:05 -0400277 c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
Hal Canary5f2807b2019-05-06 16:51:06 -0400278 },
279 [](SkCanvas* c, skiatest::Reporter* r) {
280 c->clear(kColor);
281 },
282 [](SkCanvas* c, skiatest::Reporter* r) {
283 int saveCount = c->getSaveCount();
284 c->save();
285 c->translate(SkIntToScalar(1), SkIntToScalar(2));
286 c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
287 c->restore();
288 REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
289 REPORTER_ASSERT(r, c->getTotalMatrix().isIdentity());
290 //REPORTER_ASSERT(reporter, c->getTotalClip() != kTestRegion);
291 },
292 [](SkCanvas* c, skiatest::Reporter* r) {
293 int saveCount = c->getSaveCount();
294 c->saveLayer(nullptr, nullptr);
295 c->restore();
296 REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
297 },
298 [](SkCanvas* c, skiatest::Reporter* r) {
299 int saveCount = c->getSaveCount();
300 c->saveLayer(&kRect, nullptr);
301 c->restore();
302 REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
303 },
304 [](SkCanvas* c, skiatest::Reporter* r) {
305 int saveCount = c->getSaveCount();
306 SkPaint p;
307 c->saveLayer(nullptr, &p);
308 c->restore();
309 REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
310 },
311 [](SkCanvas* c, skiatest::Reporter* r) {
312 // This test exercises a functionality in SkPicture that leads to the
313 // recording of restore offset placeholders. This test will trigger an
314 // assertion at playback time if the placeholders are not properly
315 // filled when the recording ends.
316 c->clipRect(kRect);
317 c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
318 },
319 [](SkCanvas* c, skiatest::Reporter* r) {
320 // exercise fix for http://code.google.com/p/skia/issues/detail?id=560
321 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
322 SkPaint paint;
323 paint.setStrokeWidth(SkIntToScalar(1));
324 paint.setStyle(SkPaint::kStroke_Style);
325 SkPath path;
326 path.moveTo(SkPoint{ 0, 0 });
327 path.lineTo(SkPoint{ 0, SK_ScalarNearlyZero });
328 path.lineTo(SkPoint{ SkIntToScalar(1), 0 });
329 path.lineTo(SkPoint{ SkIntToScalar(1), SK_ScalarNearlyZero/2 });
330 // test nearly zero length path
331 c->drawPath(path, paint);
332 },
333 [](SkCanvas* c, skiatest::Reporter* r) {
334 SkPictureRecorder recorder;
Mike Reed457c6dd2020-08-21 13:42:01 -0400335 SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth),
336 SkIntToScalar(kHeight));
Hal Canary5f2807b2019-05-06 16:51:06 -0400337 testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
338 testCanvas->clipRect(kRect);
339 testCanvas->drawRect(kRect, SkPaint());
340 c->drawPicture(recorder.finishRecordingAsPicture());
341 },
342 [](SkCanvas* c, skiatest::Reporter* r) {
343 int baseSaveCount = c->getSaveCount();
344 int n = c->save();
345 REPORTER_ASSERT(r, baseSaveCount == n);
346 REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
347 c->save();
348 c->save();
349 REPORTER_ASSERT(r, baseSaveCount + 3 == c->getSaveCount());
350 c->restoreToCount(baseSaveCount + 1);
351 REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
Ben Wagnercbf6d302019-05-06 15:13:30 -0400352
Hal Canary5f2807b2019-05-06 16:51:06 -0400353 // should this pin to 1, or be a no-op, or crash?
354 c->restoreToCount(0);
355 REPORTER_ASSERT(r, 1 == c->getSaveCount());
356 },
357 [](SkCanvas* c, skiatest::Reporter* r) {
358 // This test step challenges the TestDeferredCanvasStateConsistency
359 // test cases because the opaque paint can trigger an optimization
360 // that discards previously recorded commands. The challenge is to maintain
361 // correct clip and matrix stack state.
362 c->resetMatrix();
363 c->rotate(SkIntToScalar(30));
364 c->save();
365 c->translate(SkIntToScalar(2), SkIntToScalar(1));
366 c->save();
367 c->scale(SkIntToScalar(3), SkIntToScalar(3));
368 SkPaint paint;
369 paint.setColor(0xFFFFFFFF);
370 c->drawPaint(paint);
371 c->restore();
372 c->restore();
373 },
374 [](SkCanvas* c, skiatest::Reporter* r) {
375 // This test step challenges the TestDeferredCanvasStateConsistency
376 // test case because the canvas flush on a deferred canvas will
377 // reset the recording session. The challenge is to maintain correct
378 // clip and matrix stack state on the playback canvas.
379 c->resetMatrix();
380 c->rotate(SkIntToScalar(30));
381 c->save();
382 c->translate(SkIntToScalar(2), SkIntToScalar(1));
383 c->save();
384 c->scale(SkIntToScalar(3), SkIntToScalar(3));
385 c->drawRect(kRect, SkPaint());
386 c->flush();
387 c->restore();
388 c->restore();
389 },
390 [](SkCanvas* c, skiatest::Reporter* r) {
391 SkPoint pts[4];
392 pts[0].set(0, 0);
393 pts[1].set(SkIntToScalar(kWidth), 0);
394 pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
395 pts[3].set(0, SkIntToScalar(kHeight));
396 SkPaint paint;
397 SkBitmap bitmap(make_n32_bitmap(kWidth, kHeight, 0x05060708));
Mike Reed82abece2020-12-12 09:51:11 -0500398 paint.setShader(bitmap.makeShader(SkSamplingOptions()));
Hal Canary5f2807b2019-05-06 16:51:06 -0400399 c->drawVertices(
400 SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, nullptr),
401 SkBlendMode::kModulate, paint);
402 }
403};
Ben Wagnercbf6d302019-05-06 15:13:30 -0400404
Hal Canary5f2807b2019-05-06 16:51:06 -0400405DEF_TEST(Canvas_bitmap, reporter) {
406 for (const CanvasTest& test : kCanvasTests) {
407 SkBitmap referenceStore = make_n32_bitmap(kWidth, kHeight);
Ben Wagnercbf6d302019-05-06 15:13:30 -0400408 SkCanvas referenceCanvas(referenceStore);
Hal Canary5f2807b2019-05-06 16:51:06 -0400409 test(&referenceCanvas, reporter);
410 }
411}
Ben Wagnercbf6d302019-05-06 15:13:30 -0400412
Hal Canary5f2807b2019-05-06 16:51:06 -0400413DEF_TEST(Canvas_pdf, reporter) {
414 for (const CanvasTest& test : kCanvasTests) {
415 SkNullWStream outStream;
416 if (auto doc = SkPDF::MakeDocument(&outStream)) {
417 SkCanvas* canvas = doc->beginPage(SkIntToScalar(kWidth),
418 SkIntToScalar(kHeight));
419 REPORTER_ASSERT(reporter, canvas);
420 test(canvas, reporter);
edisonn@google.com77909122012-10-18 15:58:23 +0000421 }
junov@chromium.org1cc8f6f2012-02-22 21:00:42 +0000422 }
reed@google.com37f3ae02011-11-28 16:06:04 +0000423}
reedf0090cb2014-11-26 08:55:51 -0800424
425DEF_TEST(Canvas_SaveState, reporter) {
426 SkCanvas canvas(10, 10);
427 REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
428
429 int n = canvas.save();
430 REPORTER_ASSERT(reporter, 1 == n);
431 REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
432
halcanary96fcdcc2015-08-27 07:41:13 -0700433 n = canvas.saveLayer(nullptr, nullptr);
reedf0090cb2014-11-26 08:55:51 -0800434 REPORTER_ASSERT(reporter, 2 == n);
435 REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
halcanary9d524f22016-03-29 09:03:52 -0700436
reedf0090cb2014-11-26 08:55:51 -0800437 canvas.restore();
438 REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
439 canvas.restore();
440 REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
441}
reedc1b11f12015-03-13 08:48:26 -0700442
443DEF_TEST(Canvas_ClipEmptyPath, reporter) {
444 SkCanvas canvas(10, 10);
445 canvas.save();
446 SkPath path;
447 canvas.clipPath(path);
448 canvas.restore();
449 canvas.save();
450 path.moveTo(5, 5);
451 canvas.clipPath(path);
452 canvas.restore();
453 canvas.save();
454 path.moveTo(7, 7);
455 canvas.clipPath(path); // should not assert here
456 canvas.restore();
457}
fmalitaf433bb22015-08-17 08:05:13 -0700458
459namespace {
460
461class MockFilterCanvas : public SkPaintFilterCanvas {
462public:
463 MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
464
465protected:
Ben Wagnerf55fa0d2018-08-27 18:11:57 -0400466 bool onFilter(SkPaint&) const override { return true; }
fmalitaf433bb22015-08-17 08:05:13 -0700467
468private:
John Stiles7571f9e2020-09-02 22:42:33 -0400469 using INHERITED = SkPaintFilterCanvas;
fmalitaf433bb22015-08-17 08:05:13 -0700470};
471
472} // anonymous namespace
473
474// SkPaintFilterCanvas should inherit the initial target canvas state.
475DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) {
476 SkCanvas canvas(100, 100);
477 canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75));
478 canvas.scale(0.5f, 0.75f);
479
fmalitaf433bb22015-08-17 08:05:13 -0700480 MockFilterCanvas filterCanvas(&canvas);
481 REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
Mike Reed918e1442017-01-23 11:39:45 -0500482 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
fmalitaf433bb22015-08-17 08:05:13 -0700483
484 filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100));
485 filterCanvas.scale(0.75f, 0.5f);
486 REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
Mike Reed918e1442017-01-23 11:39:45 -0500487 REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
fmalitaf433bb22015-08-17 08:05:13 -0700488}
reedbabc3de2016-07-08 08:43:27 -0700489
490///////////////////////////////////////////////////////////////////////////////////////////////////
491
Ben Wagnercbf6d302019-05-06 15:13:30 -0400492namespace {
493
Mike Reed584ca892016-11-15 11:52:55 -0500494// Subclass that takes a bool*, which it updates in its construct (true) and destructor (false)
495// to allow the caller to know how long the object is alive.
496class LifeLineCanvas : public SkCanvas {
497 bool* fLifeLine;
498public:
499 LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
500 *fLifeLine = true;
501 }
Brian Salomond0072812020-07-21 17:03:56 -0400502 ~LifeLineCanvas() override {
Mike Reed584ca892016-11-15 11:52:55 -0500503 *fLifeLine = false;
504 }
505};
506
John Stilesa6841be2020-08-06 14:11:56 -0400507} // namespace
Ben Wagnercbf6d302019-05-06 15:13:30 -0400508
Mike Reed584ca892016-11-15 11:52:55 -0500509// Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
510DEF_TEST(NWayCanvas, r) {
511 const int w = 10;
512 const int h = 10;
513 bool life[2];
514 {
515 LifeLineCanvas c0(w, h, &life[0]);
516 REPORTER_ASSERT(r, life[0]);
517 }
518 REPORTER_ASSERT(r, !life[0]);
519
520
521 std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
522 std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
523 REPORTER_ASSERT(r, life[0]);
524 REPORTER_ASSERT(r, life[1]);
525
526 {
527 SkNWayCanvas nway(w, h);
528 nway.addCanvas(c0.get());
529 nway.addCanvas(c1.get());
530 REPORTER_ASSERT(r, life[0]);
531 REPORTER_ASSERT(r, life[1]);
532 }
533 // Now assert that the death of the nway has NOT also killed the sub-canvases
534 REPORTER_ASSERT(r, life[0]);
535 REPORTER_ASSERT(r, life[1]);
536}
537
538// Check that CanvasStack DOES manage the lifetime of its sub-canvases
539DEF_TEST(CanvasStack, r) {
540 const int w = 10;
541 const int h = 10;
542 bool life[2];
543 std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
544 std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
545 REPORTER_ASSERT(r, life[0]);
546 REPORTER_ASSERT(r, life[1]);
547
548 {
549 SkCanvasStack stack(w, h);
550 stack.pushCanvas(std::move(c0), {0,0});
551 stack.pushCanvas(std::move(c1), {0,0});
552 REPORTER_ASSERT(r, life[0]);
553 REPORTER_ASSERT(r, life[1]);
554 }
555 // Now assert that the death of the canvasstack has also killed the sub-canvases
556 REPORTER_ASSERT(r, !life[0]);
557 REPORTER_ASSERT(r, !life[1]);
558}
Mike Reedcd667b62017-03-02 15:21:11 -0500559
Mike Reed3b4c22d2017-03-02 20:07:46 -0500560static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
Mike Reedcd667b62017-03-02 15:21:11 -0500561 REPORTER_ASSERT(r, !canvas->isClipEmpty());
562 REPORTER_ASSERT(r, canvas->isClipRect());
563
564 canvas->save();
565 canvas->clipRect({0, 0, 0, 0});
566 REPORTER_ASSERT(r, canvas->isClipEmpty());
567 REPORTER_ASSERT(r, !canvas->isClipRect());
568 canvas->restore();
569
570 canvas->save();
571 canvas->clipRect({2, 2, 6, 6});
572 REPORTER_ASSERT(r, !canvas->isClipEmpty());
573 REPORTER_ASSERT(r, canvas->isClipRect());
574 canvas->restore();
575
576 canvas->save();
577 canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference); // punch a hole in the clip
578 REPORTER_ASSERT(r, !canvas->isClipEmpty());
579 REPORTER_ASSERT(r, !canvas->isClipRect());
580 canvas->restore();
581
582 REPORTER_ASSERT(r, !canvas->isClipEmpty());
583 REPORTER_ASSERT(r, canvas->isClipRect());
584}
Mike Reed3b4c22d2017-03-02 20:07:46 -0500585
586DEF_TEST(CanvasClipType, r) {
587 // test rasterclip backend
588 test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
589
590 // test clipstack backend
591 SkDynamicMemoryWStream stream;
Hal Canary23564b92018-09-07 14:33:14 -0400592 if (auto doc = SkPDF::MakeDocument(&stream)) {
Hal Canary4125b612018-03-20 14:17:00 -0400593 test_cliptype(doc->beginPage(100, 100), r);
594 }
Mike Reed3b4c22d2017-03-02 20:07:46 -0500595}
Matt Sarett31f99ce2017-04-11 08:46:01 -0400596
597#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
598DEF_TEST(Canvas_LegacyColorBehavior, r) {
Brian Osman82ebe042019-01-04 17:03:00 -0500599 sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
600 SkNamedGamut::kAdobeRGB);
Matt Sarett31f99ce2017-04-11 08:46:01 -0400601
602 // Make a Adobe RGB bitmap.
603 SkBitmap bitmap;
604 bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs));
605 bitmap.eraseColor(0xFF000000);
606
607 // Wrap it in a legacy canvas. Test that the canvas behaves like a legacy canvas.
608 SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy);
609 REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace());
610 SkPaint p;
611 p.setColor(SK_ColorRED);
612 canvas.drawIRect(SkIRect::MakeWH(1, 1), p);
613 REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0)));
614}
615#endif
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800616
617namespace {
618
Michael Ludwig8ee6cf32019-08-02 09:57:04 -0400619class ZeroBoundsImageFilter : public SkImageFilter_Base {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800620public:
621 static sk_sp<SkImageFilter> Make() { return sk_sp<SkImageFilter>(new ZeroBoundsImageFilter); }
622
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800623protected:
Michael Ludwige30a4852019-08-14 14:35:42 -0400624 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint*) const override {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800625 return nullptr;
626 }
Robert Phillips12078432018-05-17 11:17:39 -0400627 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&,
628 MapDirection, const SkIRect* inputRect) const override {
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800629 return SkIRect::MakeEmpty();
630 }
631
632private:
Mike Klein4fee3232018-10-18 17:27:16 -0400633 SK_FLATTENABLE_HOOKS(ZeroBoundsImageFilter)
634
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800635 ZeroBoundsImageFilter() : INHERITED(nullptr, 0, nullptr) {}
636
John Stiles7571f9e2020-09-02 22:42:33 -0400637 using INHERITED = SkImageFilter_Base;
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800638};
639
640sk_sp<SkFlattenable> ZeroBoundsImageFilter::CreateProc(SkReadBuffer& buffer) {
641 SkDEBUGFAIL("Should never get here");
642 return nullptr;
643}
644
Xianzhu Wang3b428cb2018-01-22 09:51:13 -0800645} // anonymous namespace
646
647DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter, r) {
648 SkCanvas canvas(10, 10);
649 SkPaint p;
650 p.setImageFilter(ZeroBoundsImageFilter::Make());
651 // This should not fail any assert.
652 canvas.saveLayer(nullptr, &p);
653 REPORTER_ASSERT(r, canvas.getDeviceClipBounds().isEmpty());
654 canvas.restore();
655}
Mike Reed490aa592018-04-13 15:34:16 -0400656
Mike Reed490aa592018-04-13 15:34:16 -0400657// Test that we don't crash/assert when building a canvas with degenerate coordintes
658// (esp. big ones, that might invoke tiling).
659DEF_TEST(Canvas_degenerate_dimension, reporter) {
660 // Need a paint that will sneak us past the quickReject in SkCanvas, so we can test the
661 // raster code further downstream.
662 SkPaint paint;
Michael Ludwig7d0f8532020-10-07 15:27:20 -0400663 paint.setImageFilter(SkImageFilters::Shader(SkShaders::Color(SK_ColorBLACK), nullptr));
Mike Reed490aa592018-04-13 15:34:16 -0400664 REPORTER_ASSERT(reporter, !paint.canComputeFastBounds());
665
666 const int big = 100 * 1024; // big enough to definitely trigger tiling
667 const SkISize sizes[] {SkISize{0, big}, {big, 0}, {0, 0}};
668 for (SkISize size : sizes) {
669 SkBitmap bm;
670 bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height()));
671 SkCanvas canvas(bm);
672 canvas.drawRect({0, 0, 100, 90*1024}, paint);
673 }
674}
675
Robert Phillips3d0e8502018-04-20 10:27:27 -0400676DEF_TEST(Canvas_ClippedOutImageFilter, reporter) {
677 SkCanvas canvas(100, 100);
678
679 SkPaint p;
680 p.setColor(SK_ColorGREEN);
Michael Ludwig55edb502019-08-05 10:41:10 -0400681 p.setImageFilter(SkImageFilters::Blur(3.0f, 3.0f, nullptr, nullptr));
Robert Phillips3d0e8502018-04-20 10:27:27 -0400682
683 SkRect blurredRect = SkRect::MakeXYWH(60, 10, 30, 30);
684
685 SkMatrix invM;
686 invM.setRotate(-45);
687 invM.mapRect(&blurredRect);
688
689 const SkRect clipRect = SkRect::MakeXYWH(0, 50, 50, 50);
690
691 canvas.clipRect(clipRect);
692
693 canvas.rotate(45);
694 const SkMatrix preCTM = canvas.getTotalMatrix();
695 canvas.drawRect(blurredRect, p);
696 const SkMatrix postCTM = canvas.getTotalMatrix();
697 REPORTER_ASSERT(reporter, preCTM == postCTM);
698}
699
Mike Reed7fe6ee32020-04-09 12:35:09 -0400700DEF_TEST(canvas_markctm, reporter) {
701 SkCanvas canvas(10, 10);
702
703 SkM44 m;
Brian Osman548de742020-04-24 12:02:25 -0400704 const char* id_a = "a";
705 const char* id_b = "b";
Mike Reed7fe6ee32020-04-09 12:35:09 -0400706
707 REPORTER_ASSERT(reporter, !canvas.findMarkedCTM(id_a, nullptr));
708 REPORTER_ASSERT(reporter, !canvas.findMarkedCTM(id_b, nullptr));
709
710 // remember the starting state
711 SkM44 b = canvas.getLocalToDevice();
712 canvas.markCTM(id_b);
713
714 // test add
715 canvas.concat(SkM44::Scale(2, 4, 6));
716 SkM44 a = canvas.getLocalToDevice();
717 canvas.markCTM(id_a);
718 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_a, &m) && m == a);
719
720 // test replace
721 canvas.translate(1, 2);
722 SkM44 a1 = canvas.getLocalToDevice();
723 SkASSERT(a != a1);
724 canvas.markCTM(id_a);
725 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_a, &m) && m == a1);
726
727 // test nested
728 canvas.save();
729 // no change
730 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_b, &m) && m == b);
731 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_a, &m) && m == a1);
732 canvas.translate(2, 3);
733 SkM44 a2 = canvas.getLocalToDevice();
734 SkASSERT(a2 != a1);
735 canvas.markCTM(id_a);
736 // found the new one
737 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_a, &m) && m == a2);
738 canvas.restore();
739 // found the previous one
740 REPORTER_ASSERT(reporter, canvas.findMarkedCTM(id_a, &m) && m == a1);
741}
Mike Reed8933de72021-07-25 15:21:05 -0400742
743DEF_TEST(canvas_savelayer_destructor, reporter) {
744 // What should happen in our destructor if we have unbalanced saveLayers?
745
746 SkPMColor pixels[16];
747 const SkImageInfo info = SkImageInfo::MakeN32Premul(4, 4);
748 SkPixmap pm(info, pixels, 4 * sizeof(SkPMColor));
749
750 // check all of the pixel values in pm
751 auto check_pixels = [&](SkColor expected) {
752 const SkPMColor pmc = SkPreMultiplyColor(expected);
753 for (int y = 0; y < pm.info().height(); ++y) {
754 for (int x = 0; x < pm.info().width(); ++x) {
Mike Reed95c97342021-07-26 15:33:30 -0400755 if (*pm.addr32(x, y) != pmc) {
756 ERRORF(reporter, "check_pixels_failed");
757 return;
758 }
Mike Reed8933de72021-07-25 15:21:05 -0400759 }
760 }
761 };
762
Mike Reed95c97342021-07-26 15:33:30 -0400763 auto do_test = [&](int saveCount, int restoreCount) {
764 SkASSERT(restoreCount <= saveCount);
765
Mike Reed8933de72021-07-25 15:21:05 -0400766 auto surf = SkSurface::MakeRasterDirect(pm);
767 auto canvas = surf->getCanvas();
768
769 canvas->clear(SK_ColorRED);
770 check_pixels(SK_ColorRED);
771
Mike Reed95c97342021-07-26 15:33:30 -0400772 for (int i = 0; i < saveCount; ++i) {
773 canvas->saveLayer(nullptr, nullptr);
774 }
775
Mike Reed8933de72021-07-25 15:21:05 -0400776 canvas->clear(SK_ColorBLUE);
Mike Reed95c97342021-07-26 15:33:30 -0400777 // so far, we still expect to see the red, since the blue was drawn in a layer
Mike Reed8933de72021-07-25 15:21:05 -0400778 check_pixels(SK_ColorRED);
779
Mike Reed95c97342021-07-26 15:33:30 -0400780 for (int i = 0; i < restoreCount; ++i) {
Mike Reed8933de72021-07-25 15:21:05 -0400781 canvas->restore();
782 }
783 // by returning, we are implicitly deleting the surface, and its associated canvas
784 };
785
Mike Reed95c97342021-07-26 15:33:30 -0400786 do_test(1, 1);
Mike Reed8933de72021-07-25 15:21:05 -0400787 // since we called restore, we expect to see now see blue
788 check_pixels(SK_ColorBLUE);
789
Mike Reed95c97342021-07-26 15:33:30 -0400790 // Now repeat that, but delete the canvas before we restore it
791 do_test(1, 0);
792 // We don't blit the unbalanced saveLayers, so we expect to see red (not the layer's blue)
793 check_pixels(SK_ColorRED);
794
795 // Finally, test with multiple unbalanced saveLayers. This led to a crash in an earlier
796 // implementation (crbug.com/1238731)
797 do_test(2, 0);
798 check_pixels(SK_ColorRED);
Mike Reed8933de72021-07-25 15:21:05 -0400799}