Flutter Engine
The Flutter Engine
CanvasTest.cpp
Go to the documentation of this file.
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
20#include "include/core/SkPath.h"
24#include "include/core/SkRect.h"
30#include "include/core/SkSize.h"
41#include "src/core/SkRecord.h"
42#include "src/core/SkRecords.h"
44#include "tests/Test.h"
45
46#include <cstddef>
47#include <initializer_list>
48#include <memory>
49#include <utility>
50
51using namespace skia_private;
52
53class SkPicture;
54
55#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
58#endif
59
60#ifdef SK_SUPPORT_PDF
62#endif
63
64#if defined(SK_GANESH)
66#endif
67
68#if defined(SK_GRAPHITE)
71#endif
72
75
76 template <typename T>
78 REPORTER_ASSERT(r, false, "unexpected record");
79 return {1,1,0,0};
80 }
81
82 SkRect operator()(const SkRecords::ClipRect& op) {
83 return op.rect;
84 }
85};
86
87DEF_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}}) {
96 rec.beginRecording({0,0,10,10})
97 ->clipRect(clip);
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
109DEF_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));
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)));
123 REPORTER_ASSERT(reporter, rect == rect2);
124
125 canvas.clipRect(SkRect::MakeEmpty());
126
127 irect = canvas.getDeviceClipBounds();
130 REPORTER_ASSERT(reporter, irect == irect2);
131
132 rect = canvas.getLocalClipBounds();
135 REPORTER_ASSERT(reporter, rect == rect2);
136
137 // Test for wacky sizes that we (historically) have guarded against
138 {
139 SkCanvas c(-10, -20);
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)
149template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
150 proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
151
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
160const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
161
162static 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 };
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 */
182DEF_TEST(canvas_clip_restriction, reporter) {
183 multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
184 [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
185}
186
187DEF_TEST(canvas_empty_clip, reporter) {
188 multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
189 canvas->save();
190 canvas->clipRect({0, 0, 20, 40 });
192 canvas->clipRect({30, 0, 50, 40 });
194 });
195}
196
197#endif // SK_SUPPORT_PDF
198
199DEF_TEST(CanvasNewRasterTest, reporter) {
201 const size_t minRowBytes = info.minRowBytes();
202 const size_t size = info.computeByteSize(minRowBytes);
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;
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) {
218 }
219 addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
220 }
221
222 // unaligned rowBytes
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
240 canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
241 REPORTER_ASSERT(reporter, nullptr == canvas);
242}
243
245 SkPath path;
246 path.addRect(r);
247 return path;
248}
249
252 region.setRect(r);
253 return region;
254}
255
257 SkBitmap bm;
258 bm.allocN32Pixels(w, h);
259 bm.eraseColor(c);
260 return bm;
261}
262
263// Constants used by test steps
264static constexpr SkRect kRect = {0, 0, 2, 1};
265static constexpr SkColor kColor = 0x01020304;
266static constexpr int kWidth = 2;
267static constexpr int kHeight = 2;
268
270
272 [](SkCanvas* c, skiatest::Reporter* r) {
274 },
275 [](SkCanvas* c, skiatest::Reporter* r) {
277 },
278 [](SkCanvas* c, skiatest::Reporter* r) {
279 c->rotate(SkIntToScalar(1));
280 },
281 [](SkCanvas* c, skiatest::Reporter* r) {
283 },
284 [](SkCanvas* c, skiatest::Reporter* r) {
285 c->concat(SkMatrix::Scale(2, 3));
286 },
287 [](SkCanvas* c, skiatest::Reporter* r) {
288 c->setMatrix(SkMatrix::Scale(2, 3));
289 },
290 [](SkCanvas* c, skiatest::Reporter* r) {
291 c->clipRect(kRect);
292 },
293 [](SkCanvas* c, skiatest::Reporter* r) {
294 c->clipPath(make_path_from_rect(SkRect{0, 0, 2, 1}));
295 },
296 [](SkCanvas* c, skiatest::Reporter* r) {
298 },
299 [](SkCanvas* c, skiatest::Reporter* r) {
300 c->clear(kColor);
301 },
302 [](SkCanvas* c, skiatest::Reporter* r) {
303 int saveCount = c->getSaveCount();
304 c->save();
307 c->restore();
308 REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
310 //REPORTER_ASSERT(reporter, c->getTotalClip() != kTestRegion);
311 },
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 },
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 },
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 },
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);
338 },
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')
343 paint.setStrokeWidth(SkIntToScalar(1));
345 SkPath path;
346 path.moveTo(SkPoint{ 0, 0 });
347 path.lineTo(SkPoint{ 0, SK_ScalarNearlyZero });
348 path.lineTo(SkPoint{ SkIntToScalar(1), 0 });
350 // test nearly zero length path
351 c->drawPath(path, paint);
352 },
353 [](SkCanvas* c, skiatest::Reporter* r) {
354 SkPictureRecorder recorder;
355 SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth),
357 testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
358 testCanvas->clipRect(kRect);
359 testCanvas->drawRect(kRect, SkPaint());
361 },
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 },
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();
386 c->save();
389 paint.setColor(0xFFFFFFFF);
390 c->drawPaint(paint);
391 c->restore();
392 c->restore();
393 },
394 [](SkCanvas* c, skiatest::Reporter* r) {
395 SkPoint pts[4];
396 pts[0].set(0, 0);
397 pts[1].set(SkIntToScalar(kWidth), 0);
399 pts[3].set(0, SkIntToScalar(kHeight));
402 paint.setShader(bitmap.makeShader(SkSamplingOptions()));
403 c->drawVertices(
406 }
407};
408
409DEF_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
418DEF_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),
424 REPORTER_ASSERT(reporter, canvas);
425 test(canvas, reporter);
426 }
427 }
428}
429#endif
430
431DEF_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
449DEF_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
465namespace {
466
467class MockFilterCanvas : public SkPaintFilterCanvas {
468public:
469 MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
470
471protected:
472 bool onFilter(SkPaint&) const override { return true; }
473
474private:
476};
477
478} // anonymous namespace
479
480// SkPaintFilterCanvas should inherit the initial target canvas state.
481DEF_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
498namespace {
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.
502class LifeLineCanvas : public SkCanvas {
503 bool* fLifeLine;
504public:
505 LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
506 *fLifeLine = true;
507 }
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
516DEF_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
545DEF_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
566static 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
592DEF_TEST(CanvasClipType, r) {
593 // test rasterclip backend
595
596#ifdef SK_SUPPORT_PDF
597 // test clipstack backend
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
606DEF_TEST(Canvas_LegacyColorBehavior, r) {
609
610 // Make a Adobe RGB 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);
622}
623#endif
624
625DEF_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);
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).
637DEF_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.
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
654DEF_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
678DEF_TEST(canvas_savelayer_destructor, reporter) {
679 // What should happen in our destructor if we have unbalanced saveLayers?
680
681 SkPMColor pixels[16];
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);
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
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
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)
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);
734}
735
736DEF_TEST(Canvas_saveLayer_colorSpace, reporter) {
737 SkColor pixels[1];
739 SkPixmap pm(info, pixels, sizeof(SkColor));
740
741 auto surf = SkSurfaces::WrapPixels(pm);
742 auto canvas = surf->getCanvas();
743
745 canvas->saveLayer(SkCanvas::SaveLayerRec(nullptr, nullptr, nullptr, cs.get(), 0));
747 paint.setColor(SK_ColorRED);
748 canvas->drawPaint(paint);
749 canvas->restore();
750
752}
753
754// Draw a lot of rectangles with different colors. On the GPU, the different colors make this
755// relatively difficult to batch.
757 SkCanvas* canvas = surface->getCanvas();
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)
767 reporter,
768 contextInfo,
773 GrDirectContext* context = contextInfo.directContext();
776}
777#endif
778
779#if defined(SK_GRAPHITE)
780DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(TestManyDrawsGraphite, reporter, context,
782 using namespace skgpu::graphite;
786 std::unique_ptr<Recorder> recorder = context->makeRecorder();
789}
790#endif
void test_many_draws(skiatest::Reporter *reporter, SkSurface *surface)
Definition: CanvasTest.cpp:756
static CanvasTest kCanvasTests[]
Definition: CanvasTest.cpp:271
static void test_cliptype(SkCanvas *canvas, skiatest::Reporter *r)
Definition: CanvasTest.cpp:566
static constexpr SkColor kColor
Definition: CanvasTest.cpp:265
static SkRegion make_region_from_irect(SkIRect r)
Definition: CanvasTest.cpp:250
static SkBitmap make_n32_bitmap(int w, int h, SkColor c=SK_ColorWHITE)
Definition: CanvasTest.cpp:256
static constexpr SkRect kRect
Definition: CanvasTest.cpp:264
static SkPath make_path_from_rect(SkRect r)
Definition: CanvasTest.cpp:244
void(*)(SkCanvas *, skiatest::Reporter *) CanvasTest
Definition: CanvasTest.cpp:269
static constexpr int kWidth
Definition: CanvasTest.cpp:266
static constexpr int kHeight
Definition: CanvasTest.cpp:267
DEF_TEST(canvas_unsorted_clip, r)
Definition: CanvasTest.cpp:87
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define test(name)
sk_bzero(glyphs, sizeof(glyphs))
reporter
Definition: FontMgrTest.cpp:39
bool check_pixels(skiatest::Reporter *reporter, GrDirectContext *dContext, const GrBackendTexture &tex, const SkImageInfo &info, SkColor expectedColor)
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kModulate
r = s*d
static SkPMColor SkSwizzle_BGRA_to_PMColor(uint32_t c)
Definition: SkColorData.h:91
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
@ kUnknown_SkColorType
uninitialized
Definition: SkColorType.h:20
SK_API SkPMColor SkPreMultiplyColor(SkColor c)
Definition: SkColor.cpp:21
uint32_t SkColor
Definition: SkColor.h:37
uint32_t SkPMColor
Definition: SkColor.h:205
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(name, reporter, graphite_context, ctsEnforcement)
Definition: Test.h:377
#define ERRORF(r,...)
Definition: Test.h:293
#define DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info, ctsEnforcement)
Definition: Test.h:434
void allocN32Pixels(int width, int height, bool isOpaque=false)
Definition: SkBitmap.cpp:232
bool setInfo(const SkImageInfo &imageInfo, size_t rowBytes=0)
Definition: SkBitmap.cpp:114
void eraseColor(SkColor4f) const
Definition: SkBitmap.cpp:442
void pushCanvas(std::unique_ptr< SkCanvas >, const SkIPoint &origin)
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1361
virtual bool isClipEmpty() const
Definition: SkCanvas.cpp:1549
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void androidFramework_setDeviceClipRestriction(const SkIRect &rect)
Definition: SkCanvas.cpp:1378
SkRect getLocalClipBounds() const
Definition: SkCanvas.cpp:1586
virtual bool isClipRect() const
Definition: SkCanvas.cpp:1553
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
virtual SkISize getBaseLayerSize() const
Definition: SkCanvas.cpp:369
void clipRegion(const SkRegion &deviceRgn, SkClipOp op=SkClipOp::kIntersect)
Definition: SkCanvas.cpp:1510
void drawIRect(const SkIRect &rect, const SkPaint &paint)
Definition: SkCanvas.h:1358
void clear(SkColor color)
Definition: SkCanvas.h:1199
int getSaveCount() const
Definition: SkCanvas.cpp:431
void rotate(SkScalar degrees)
Definition: SkCanvas.cpp:1300
void restoreToCount(int saveCount)
Definition: SkCanvas.cpp:478
SkMatrix getTotalMatrix() const
Definition: SkCanvas.cpp:1629
void resetMatrix()
Definition: SkCanvas.cpp:1355
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1456
SkIRect getDeviceClipBounds() const
Definition: SkCanvas.cpp:1607
int save()
Definition: SkCanvas.cpp:447
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void setMatrix(const SkM44 &matrix)
Definition: SkCanvas.cpp:1349
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
void drawPicture(const SkPicture *picture)
Definition: SkCanvas.h:1961
static std::unique_ptr< SkCanvas > MakeRasterDirect(const SkImageInfo &info, void *pixels, size_t rowBytes, const SkSurfaceProps *props=nullptr)
Definition: SkCanvas.cpp:2801
void drawVertices(const SkVertices *vertices, SkBlendMode mode, const SkPaint &paint)
Definition: SkCanvas.cpp:1720
SkImageInfo imageInfo() const
Definition: SkCanvas.cpp:1206
void skew(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1312
static sk_sp< SkColorSpace > MakeSRGB()
sk_sp< SkColorSpace > makeColorSpin() const
static sk_sp< SkColorSpace > MakeRGB(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &toXYZ)
static sk_sp< SkImageFilter > Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Empty()
static sk_sp< SkImageFilter > Shader(sk_sp< SkShader > shader, const CropRect &cropRect={})
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:452
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
bool isIdentity() const
Definition: SkMatrix.h:223
virtual void addCanvas(SkCanvas *)
SkPaintFilterCanvas(SkCanvas *canvas)
virtual bool onFilter(SkPaint &paint) const =0
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
Definition: SkPath.h:59
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
SkColor getColor(int x, int y) const
Definition: SkPixmap.cpp:187
const uint32_t * addr32() const
Definition: SkPixmap.h:352
size_t rowBytes() const
Definition: SkPixmap.h:145
const SkImageInfo & info() const
Definition: SkPixmap.h:135
auto visit(int i, F &&f) const -> decltype(f(SkRecords::NoOp()))
Definition: SkRecord.h:45
int count() const
Definition: SkRecord.h:38
bool setRect(const SkIRect &rect)
Definition: SkRegion.cpp:192
static sk_sp< SkVertices > MakeCopy(VertexMode mode, int vertexCount, const SkPoint positions[], const SkPoint texs[], const SkColor colors[], int indexCount, const uint16_t indices[])
Definition: SkVertices.cpp:200
@ kTriangleFan_VertexMode
Definition: SkVertices.h:33
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
VkSurfaceKHR surface
Definition: main.cc:49
double y
double x
static constexpr skcms_Matrix3x3 kAdobeRGB
Definition: SkColorSpace.h:77
static constexpr skcms_TransferFunction kSRGB
Definition: SkColorSpace.h:45
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
ClipOpAndAA opAA SkRegion region
Definition: SkRecords.h:238
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkShader > Color(SkColor)
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
SK_API sk_sp< SkSurface > WrapPixels(const SkImageInfo &imageInfo, void *pixels, size_t rowBytes, const SkSurfaceProps *surfaceProps=nullptr)
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
Definition: bitmap.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SkScalar w
SkScalar h
#define T
Definition: precompiler.cc:65
int32_t height
int32_t width
SkRect operator()(const SkRecords::ClipRect &op)
Definition: CanvasTest.cpp:82
skiatest::Reporter * r
Definition: CanvasTest.cpp:74
SkRect operator()(const T &)
Definition: CanvasTest.cpp:77
Definition: SkMD5.cpp:120
Definition: SkRect.h:32
constexpr int32_t height() const
Definition: SkRect.h:165
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
constexpr int32_t width() const
Definition: SkRect.h:158
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
bool isEmpty() const
Definition: SkRect.h:202
Definition: SkSize.h:16
static constexpr SkISize MakeEmpty()
Definition: SkSize.h:22
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
static SkImageInfo MakeN32Premul(int width, int height)
SkColorSpace * colorSpace() const
static SkImageInfo MakeN32(int width, int height, SkAlphaType at)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
void set(float x, float y)
Definition: SkPoint_impl.h:200
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609