Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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>
77 SkRect operator()(const 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);
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;
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
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
251 SkRegion region;
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 });
349 path.lineTo(SkPoint{ SkIntToScalar(1), SK_ScalarNearlyZero/2 });
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.
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
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)
static CanvasTest kCanvasTests[]
static void test_cliptype(SkCanvas *canvas, skiatest::Reporter *r)
static constexpr SkColor kColor
static SkRegion make_region_from_irect(SkIRect r)
static SkBitmap make_n32_bitmap(int w, int h, SkColor c=SK_ColorWHITE)
static constexpr SkRect kRect
static SkPath make_path_from_rect(SkRect r)
void(*)(SkCanvas *, skiatest::Reporter *) CanvasTest
static constexpr int kWidth
static constexpr int kHeight
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define test(name)
reporter
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 void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
#define INHERITED(method,...)
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define DEF_TEST(name, reporter)
Definition Test.h:312
#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:500
void drawRect(const SkRect &rect, const SkPaint &paint)
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
virtual bool isClipEmpty() const
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
void androidFramework_setDeviceClipRestriction(const SkIRect &rect)
SkRect getLocalClipBounds() const
virtual bool isClipRect() const
void drawPaint(const SkPaint &paint)
virtual SkISize getBaseLayerSize() const
Definition SkCanvas.cpp:373
void clipRegion(const SkRegion &deviceRgn, SkClipOp op=SkClipOp::kIntersect)
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:435
void rotate(SkScalar degrees)
void restoreToCount(int saveCount)
Definition SkCanvas.cpp:482
SkMatrix getTotalMatrix() const
void resetMatrix()
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
SkIRect getDeviceClipBounds() const
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
void setMatrix(const SkM44 &matrix)
void scale(SkScalar sx, SkScalar sy)
void concat(const SkMatrix &matrix)
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)
void drawVertices(const SkVertices *vertices, SkBlendMode mode, const SkPaint &paint)
SkImageInfo imageInfo() const
void skew(SkScalar sx, SkScalar sy)
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
bool isIdentity() const
Definition SkMatrix.h:223
virtual void addCanvas(SkCanvas *)
virtual bool onFilter(SkPaint &paint) const =0
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
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[])
@ kTriangleFan_VertexMode
Definition SkVertices.h:33
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
VkSurfaceKHR surface
Definition main.cc:49
double y
double x
static constexpr skcms_Matrix3x3 kAdobeRGB
static constexpr skcms_TransferFunction kSRGB
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
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)
SkScalar w
SkScalar h
int32_t height
int32_t width
SkRect operator()(const SkRecords::ClipRect &op)
skiatest::Reporter * r
SkRect operator()(const T &)
Definition SkMD5.cpp:120
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
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)
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