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/docs/SkPDFDocument.h"
35 #include "include/effects/SkImageFilters.h"
36 #include "include/private/SkMalloc.h"
37 #include "include/private/SkTemplates.h"
38 #include "include/utils/SkNWayCanvas.h"
39 #include "include/utils/SkPaintFilterCanvas.h"
40 #include "src/core/SkBigPicture.h"
41 #include "src/core/SkImageFilter_Base.h"
42 #include "src/core/SkRecord.h"
43 #include "src/core/SkSpecialImage.h"
44 #include "src/utils/SkCanvasStack.h"
45 #include "tests/Test.h"
46
47 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
48 #include "include/core/SkColorSpace.h"
49 #include "include/private/SkColorData.h"
50 #endif
51
52 #include <memory>
53 #include <utility>
54
55 class SkReadBuffer;
56
57 struct ClipRectVisitor {
58 skiatest::Reporter* r;
59
60 template <typename T>
operator ()ClipRectVisitor61 SkRect operator()(const T&) {
62 REPORTER_ASSERT(r, false, "unexpected record");
63 return {1,1,0,0};
64 }
65
operator ()ClipRectVisitor66 SkRect operator()(const SkRecords::ClipRect& op) {
67 return op.rect;
68 }
69 };
70
DEF_TEST(canvas_unsorted_clip, r)71 DEF_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
DEF_TEST(canvas_clipbounds, reporter)93 DEF_TEST(canvas_clipbounds, reporter) {
94 SkCanvas canvas(10, 10);
95 SkIRect irect, irect2;
96 SkRect rect, rect2;
97
98 irect = canvas.getDeviceClipBounds();
99 REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
100 REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
101 REPORTER_ASSERT(reporter, irect == irect2);
102
103 // 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)));
106 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
107 REPORTER_ASSERT(reporter, rect == rect2);
108
109 canvas.clipRect(SkRect::MakeEmpty());
110
111 irect = canvas.getDeviceClipBounds();
112 REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
113 REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
114 REPORTER_ASSERT(reporter, irect == irect2);
115
116 rect = canvas.getLocalClipBounds();
117 REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
118 REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
119 REPORTER_ASSERT(reporter, rect == rect2);
120
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());
125
126 SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
127 }
128 }
129
130 // Will call proc with multiple styles of canvas (recording, raster, pdf)
multi_canvas_driver(int w, int h, F proc)131 template <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;
135 if (auto doc = SkPDF::MakeDocument(&stream)) {
136 proc(doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
137 }
138
139 proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
140 }
141
142 const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
143
test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas)144 static 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);
154 }
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 */
DEF_TEST(canvas_clip_restriction, reporter)164 DEF_TEST(canvas_clip_restriction, reporter) {
165 multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
166 [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
167 }
168
DEF_TEST(canvas_empty_clip, reporter)169 DEF_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 });
177 }
178
DEF_TEST(CanvasNewRasterTest, reporter)179 DEF_TEST(CanvasNewRasterTest, reporter) {
180 SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
181 const size_t minRowBytes = info.minRowBytes();
182 const size_t size = info.computeByteSize(minRowBytes);
183 SkAutoTMalloc<SkPMColor> storage(size);
184 SkPMColor* baseAddr = storage.get();
185 sk_bzero(baseAddr, size);
186
187 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
188 REPORTER_ASSERT(reporter, canvas);
189
190 SkPixmap pmap;
191 const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
192 REPORTER_ASSERT(reporter, addr);
193 REPORTER_ASSERT(reporter, info == pmap.info());
194 REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
195 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 }
199 addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
200 }
201
202 // unaligned rowBytes
203 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr,
204 minRowBytes + 1));
205
206 // now try a deliberately bad info
207 info = info.makeWH(-1, info.height());
208 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
209
210 // too big
211 info = info.makeWH(1 << 30, 1 << 30);
212 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
213
214 // not a valid pixel type
215 info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
216 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
217
218 // We should not succeed with a zero-sized valid info
219 info = SkImageInfo::MakeN32Premul(0, 0);
220 canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
221 REPORTER_ASSERT(reporter, nullptr == canvas);
222 }
223
make_path_from_rect(SkRect r)224 static SkPath make_path_from_rect(SkRect r) {
225 SkPath path;
226 path.addRect(r);
227 return path;
228 }
229
make_region_from_irect(SkIRect r)230 static SkRegion make_region_from_irect(SkIRect r) {
231 SkRegion region;
232 region.setRect(r);
233 return region;
234 }
235
make_n32_bitmap(int w, int h, SkColor c = SK_ColorWHITE)236 static 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 }
242
243 // Constants used by test steps
244 static constexpr SkRect kRect = {0, 0, 2, 1};
245 static constexpr SkColor kColor = 0x01020304;
246 static constexpr int kWidth = 2;
247 static constexpr int kHeight = 2;
248
249 using CanvasTest = void (*)(SkCanvas*, skiatest::Reporter*);
250
251 static 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) {
265 c->concat(SkMatrix::Scale(2, 3));
266 },
267 [](SkCanvas* c, skiatest::Reporter* r) {
268 c->setMatrix(SkMatrix::Scale(2, 3));
269 },
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) {
277 c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
278 },
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;
335 SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth),
336 SkIntToScalar(kHeight));
337 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());
352
353 // 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));
398 paint.setShader(bitmap.makeShader(SkSamplingOptions()));
399 c->drawVertices(
400 SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, nullptr),
401 SkBlendMode::kModulate, paint);
402 }
403 };
404
DEF_TEST(Canvas_bitmap, reporter)405 DEF_TEST(Canvas_bitmap, reporter) {
406 for (const CanvasTest& test : kCanvasTests) {
407 SkBitmap referenceStore = make_n32_bitmap(kWidth, kHeight);
408 SkCanvas referenceCanvas(referenceStore);
409 test(&referenceCanvas, reporter);
410 }
411 }
412
DEF_TEST(Canvas_pdf, reporter)413 DEF_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);
421 }
422 }
423 }
424
DEF_TEST(Canvas_SaveState, reporter)425 DEF_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
433 n = canvas.saveLayer(nullptr, nullptr);
434 REPORTER_ASSERT(reporter, 2 == n);
435 REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
436
437 canvas.restore();
438 REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
439 canvas.restore();
440 REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
441 }
442
DEF_TEST(Canvas_ClipEmptyPath, reporter)443 DEF_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 }
458
459 namespace {
460
461 class MockFilterCanvas : public SkPaintFilterCanvas {
462 public:
MockFilterCanvas(SkCanvas* canvas)463 MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
464
465 protected:
466 bool onFilter(SkPaint&) const override { return true; }
467
468 private:
469 using INHERITED = SkPaintFilterCanvas;
470 };
471
472 } // anonymous namespace
473
474 // SkPaintFilterCanvas should inherit the initial target canvas state.
DEF_TEST(PaintFilterCanvas_ConsistentState, reporter)475 DEF_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
480 MockFilterCanvas filterCanvas(&canvas);
481 REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
482 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
483
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());
487 REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
488 }
489
490 ///////////////////////////////////////////////////////////////////////////////////////////////////
491
492 namespace {
493
494 // 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.
496 class LifeLineCanvas : public SkCanvas {
497 bool* fLifeLine;
498 public:
LifeLineCanvas(int w, int h, bool* lifeline)499 LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
500 *fLifeLine = true;
501 }
502 ~LifeLineCanvas() override {
503 *fLifeLine = false;
504 }
505 };
506
507 } // namespace
508
509 // Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
DEF_TEST(NWayCanvas, r)510 DEF_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
DEF_TEST(CanvasStack, r)539 DEF_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 }
559
test_cliptype(SkCanvas* canvas, skiatest::Reporter* r)560 static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
561 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 }
585
DEF_TEST(CanvasClipType, r)586 DEF_TEST(CanvasClipType, r) {
587 // test rasterclip backend
588 test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
589
590 // test clipstack backend
591 SkDynamicMemoryWStream stream;
592 if (auto doc = SkPDF::MakeDocument(&stream)) {
593 test_cliptype(doc->beginPage(100, 100), r);
594 }
595 }
596
597 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
DEF_TEST(Canvas_LegacyColorBehavior, r)598 DEF_TEST(Canvas_LegacyColorBehavior, r) {
599 sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
600 SkNamedGamut::kAdobeRGB);
601
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
616
617 namespace {
618
619 class ZeroBoundsImageFilter : public SkImageFilter_Base {
620 public:
Make()621 static sk_sp<SkImageFilter> Make() { return sk_sp<SkImageFilter>(new ZeroBoundsImageFilter); }
622
623 protected:
624 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint*) const override {
625 return nullptr;
626 }
627 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&,
628 MapDirection, const SkIRect* inputRect) const override {
629 return SkIRect::MakeEmpty();
630 }
631
632 private:
633 SK_FLATTENABLE_HOOKS(ZeroBoundsImageFilter)
634
ZeroBoundsImageFilter()635 ZeroBoundsImageFilter() : INHERITED(nullptr, 0, nullptr) {}
636
637 using INHERITED = SkImageFilter_Base;
638 };
639
CreateProc(SkReadBuffer& buffer)640 sk_sp<SkFlattenable> ZeroBoundsImageFilter::CreateProc(SkReadBuffer& buffer) {
641 SkDEBUGFAIL("Should never get here");
642 return nullptr;
643 }
644
645 } // anonymous namespace
646
DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter, r)647 DEF_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 }
656
657 // Test that we don't crash/assert when building a canvas with degenerate coordintes
658 // (esp. big ones, that might invoke tiling).
DEF_TEST(Canvas_degenerate_dimension, reporter)659 DEF_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;
663 paint.setImageFilter(SkImageFilters::Shader(SkShaders::Color(SK_ColorBLACK), nullptr));
664 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
DEF_TEST(Canvas_ClippedOutImageFilter, reporter)676 DEF_TEST(Canvas_ClippedOutImageFilter, reporter) {
677 SkCanvas canvas(100, 100);
678
679 SkPaint p;
680 p.setColor(SK_ColorGREEN);
681 p.setImageFilter(SkImageFilters::Blur(3.0f, 3.0f, nullptr, nullptr));
682
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
DEF_TEST(canvas_markctm, reporter)700 DEF_TEST(canvas_markctm, reporter) {
701 SkCanvas canvas(10, 10);
702
703 SkM44 m;
704 const char* id_a = "a";
705 const char* id_b = "b";
706
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 }
742
DEF_TEST(canvas_savelayer_destructor, reporter)743 DEF_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) {
755 if (*pm.addr32(x, y) != pmc) {
756 ERRORF(reporter, "check_pixels_failed");
757 return;
758 }
759 }
760 }
761 };
762
763 auto do_test = [&](int saveCount, int restoreCount) {
764 SkASSERT(restoreCount <= saveCount);
765
766 auto surf = SkSurface::MakeRasterDirect(pm);
767 auto canvas = surf->getCanvas();
768
769 canvas->clear(SK_ColorRED);
770 check_pixels(SK_ColorRED);
771
772 for (int i = 0; i < saveCount; ++i) {
773 canvas->saveLayer(nullptr, nullptr);
774 }
775
776 canvas->clear(SK_ColorBLUE);
777 // so far, we still expect to see the red, since the blue was drawn in a layer
778 check_pixels(SK_ColorRED);
779
780 for (int i = 0; i < restoreCount; ++i) {
781 canvas->restore();
782 }
783 // by returning, we are implicitly deleting the surface, and its associated canvas
784 };
785
786 do_test(1, 1);
787 // since we called restore, we expect to see now see blue
788 check_pixels(SK_ColorBLUE);
789
790 // 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);
799 }
800