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