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