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