CanvasTest.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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. #include "include/core/SkBitmap.h"
  8. #include "include/core/SkBlendMode.h"
  9. #include "include/core/SkCanvas.h"
  10. #include "include/core/SkClipOp.h"
  11. #include "include/core/SkColor.h"
  12. #include "include/core/SkDocument.h"
  13. #include "include/core/SkFlattenable.h"
  14. #include "include/core/SkImageFilter.h"
  15. #include "include/core/SkImageInfo.h"
  16. #include "include/core/SkMatrix.h"
  17. #include "include/core/SkPaint.h"
  18. #include "include/core/SkPath.h"
  19. #include "include/core/SkPictureRecorder.h"
  20. #include "include/core/SkPixmap.h"
  21. #include "include/core/SkPoint.h"
  22. #include "include/core/SkRect.h"
  23. #include "include/core/SkRefCnt.h"
  24. #include "include/core/SkRegion.h"
  25. #include "include/core/SkScalar.h"
  26. #include "include/core/SkShader.h"
  27. #include "include/core/SkSize.h"
  28. #include "include/core/SkStream.h"
  29. #include "include/core/SkString.h"
  30. #include "include/core/SkSurface.h"
  31. #include "include/core/SkTypes.h"
  32. #include "include/core/SkVertices.h"
  33. #include "include/docs/SkPDFDocument.h"
  34. #include "include/effects/SkBlurImageFilter.h"
  35. #include "include/effects/SkPaintImageFilter.h"
  36. #include "include/private/SkMalloc.h"
  37. #include "include/private/SkTemplates.h"
  38. #include "include/utils/SkNWayCanvas.h"
  39. #include "include/utils/SkPaintFilterCanvas.h"
  40. #include "src/core/SkClipOpPriv.h"
  41. #include "src/core/SkSpecialImage.h"
  42. #include "src/utils/SkCanvasStack.h"
  43. #include "tests/Test.h"
  44. #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
  45. #include "include/core/SkColorSpace.h"
  46. #include "include/private/SkColorData.h"
  47. #endif
  48. #include <memory>
  49. #include <utility>
  50. class SkReadBuffer;
  51. DEF_TEST(canvas_clipbounds, reporter) {
  52. SkCanvas canvas(10, 10);
  53. SkIRect irect, irect2;
  54. SkRect rect, rect2;
  55. irect = canvas.getDeviceClipBounds();
  56. REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
  57. REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
  58. REPORTER_ASSERT(reporter, irect == irect2);
  59. // local bounds are always too big today -- can we trim them?
  60. rect = canvas.getLocalClipBounds();
  61. REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10)));
  62. REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
  63. REPORTER_ASSERT(reporter, rect == rect2);
  64. canvas.clipRect(SkRect::MakeEmpty());
  65. irect = canvas.getDeviceClipBounds();
  66. REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
  67. REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
  68. REPORTER_ASSERT(reporter, irect == irect2);
  69. rect = canvas.getLocalClipBounds();
  70. REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
  71. REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
  72. REPORTER_ASSERT(reporter, rect == rect2);
  73. // Test for wacky sizes that we (historically) have guarded against
  74. {
  75. SkCanvas c(-10, -20);
  76. REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty());
  77. SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
  78. }
  79. }
  80. // Will call proc with multiple styles of canvas (recording, raster, pdf)
  81. template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
  82. proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
  83. SkNullWStream stream;
  84. if (auto doc = SkPDF::MakeDocument(&stream)) {
  85. proc(doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
  86. }
  87. proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
  88. }
  89. const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
  90. static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
  91. REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);
  92. const SkIRect restrictionR = { 2, 2, 8, 8 };
  93. canvas->androidFramework_setDeviceClipRestriction(restrictionR);
  94. REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);
  95. const SkIRect clipR = { 4, 4, 6, 6 };
  96. canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
  97. REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
  98. #ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
  99. // now test that expanding clipops can't exceed the restriction
  100. const SkClipOp expanders[] = {
  101. SkClipOp::kUnion_deprecated,
  102. SkClipOp::kXOR_deprecated,
  103. SkClipOp::kReverseDifference_deprecated,
  104. SkClipOp::kReplace_deprecated,
  105. };
  106. const SkRect expandR = { 0, 0, 5, 9 };
  107. SkASSERT(!SkRect::Make(restrictionR).contains(expandR));
  108. for (SkClipOp op : expanders) {
  109. canvas->save();
  110. canvas->clipRect(expandR, op);
  111. REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds()));
  112. canvas->restore();
  113. }
  114. #endif
  115. }
  116. /**
  117. * Clip restriction logic exists in the canvas itself, and in various kinds of devices.
  118. *
  119. * This test explicitly tries to exercise that variety:
  120. * - picture : empty device but exercises canvas itself
  121. * - pdf : uses SkClipStack in its device (as does SVG and GPU)
  122. * - raster : uses SkRasterClip in its device
  123. */
  124. DEF_TEST(canvas_clip_restriction, reporter) {
  125. multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
  126. [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
  127. }
  128. DEF_TEST(canvas_empty_clip, reporter) {
  129. multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
  130. canvas->save();
  131. canvas->clipRect({0, 0, 20, 40 });
  132. REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
  133. canvas->clipRect({30, 0, 50, 40 });
  134. REPORTER_ASSERT(reporter, canvas->isClipEmpty());
  135. });
  136. }
  137. DEF_TEST(CanvasNewRasterTest, reporter) {
  138. SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
  139. const size_t minRowBytes = info.minRowBytes();
  140. const size_t size = info.computeByteSize(minRowBytes);
  141. SkAutoTMalloc<SkPMColor> storage(size);
  142. SkPMColor* baseAddr = storage.get();
  143. sk_bzero(baseAddr, size);
  144. std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
  145. REPORTER_ASSERT(reporter, canvas);
  146. SkPixmap pmap;
  147. const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
  148. REPORTER_ASSERT(reporter, addr);
  149. REPORTER_ASSERT(reporter, info == pmap.info());
  150. REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
  151. for (int y = 0; y < info.height(); ++y) {
  152. for (int x = 0; x < info.width(); ++x) {
  153. REPORTER_ASSERT(reporter, 0 == addr[x]);
  154. }
  155. addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
  156. }
  157. // now try a deliberately bad info
  158. info = info.makeWH(-1, info.height());
  159. REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
  160. // too big
  161. info = info.makeWH(1 << 30, 1 << 30);
  162. REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
  163. // not a valid pixel type
  164. info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
  165. REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
  166. // We should not succeed with a zero-sized valid info
  167. info = SkImageInfo::MakeN32Premul(0, 0);
  168. canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
  169. REPORTER_ASSERT(reporter, nullptr == canvas);
  170. }
  171. static SkPath make_path_from_rect(SkRect r) {
  172. SkPath path;
  173. path.addRect(r);
  174. return path;
  175. }
  176. static SkRegion make_region_from_irect(SkIRect r) {
  177. SkRegion region;
  178. region.setRect(r);
  179. return region;
  180. }
  181. static SkBitmap make_n32_bitmap(int w, int h, SkColor c = SK_ColorWHITE) {
  182. SkBitmap bm;
  183. bm.allocN32Pixels(w, h);
  184. bm.eraseColor(c);
  185. return bm;
  186. }
  187. // Constants used by test steps
  188. static constexpr SkRect kRect = {0, 0, 2, 1};
  189. static constexpr SkColor kColor = 0x01020304;
  190. static constexpr int kWidth = 2;
  191. static constexpr int kHeight = 2;
  192. using CanvasTest = void (*)(SkCanvas*, skiatest::Reporter*);
  193. static CanvasTest kCanvasTests[] = {
  194. [](SkCanvas* c, skiatest::Reporter* r) {
  195. c->translate(SkIntToScalar(1), SkIntToScalar(2));
  196. },
  197. [](SkCanvas* c, skiatest::Reporter* r) {
  198. c->scale(SkIntToScalar(1), SkIntToScalar(2));
  199. },
  200. [](SkCanvas* c, skiatest::Reporter* r) {
  201. c->rotate(SkIntToScalar(1));
  202. },
  203. [](SkCanvas* c, skiatest::Reporter* r) {
  204. c->skew(SkIntToScalar(1), SkIntToScalar(2));
  205. },
  206. [](SkCanvas* c, skiatest::Reporter* r) {
  207. c->concat(SkMatrix::MakeScale(2, 3));
  208. },
  209. [](SkCanvas* c, skiatest::Reporter* r) {
  210. c->setMatrix(SkMatrix::MakeScale(2, 3));
  211. },
  212. [](SkCanvas* c, skiatest::Reporter* r) {
  213. c->clipRect(kRect);
  214. },
  215. [](SkCanvas* c, skiatest::Reporter* r) {
  216. c->clipPath(make_path_from_rect(SkRect{0, 0, 2, 1}));
  217. },
  218. [](SkCanvas* c, skiatest::Reporter* r) {
  219. c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}), kReplace_SkClipOp);
  220. },
  221. [](SkCanvas* c, skiatest::Reporter* r) {
  222. c->clear(kColor);
  223. },
  224. [](SkCanvas* c, skiatest::Reporter* r) {
  225. int saveCount = c->getSaveCount();
  226. c->save();
  227. c->translate(SkIntToScalar(1), SkIntToScalar(2));
  228. c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
  229. c->restore();
  230. REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
  231. REPORTER_ASSERT(r, c->getTotalMatrix().isIdentity());
  232. //REPORTER_ASSERT(reporter, c->getTotalClip() != kTestRegion);
  233. },
  234. [](SkCanvas* c, skiatest::Reporter* r) {
  235. int saveCount = c->getSaveCount();
  236. c->saveLayer(nullptr, nullptr);
  237. c->restore();
  238. REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
  239. },
  240. [](SkCanvas* c, skiatest::Reporter* r) {
  241. int saveCount = c->getSaveCount();
  242. c->saveLayer(&kRect, nullptr);
  243. c->restore();
  244. REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
  245. },
  246. [](SkCanvas* c, skiatest::Reporter* r) {
  247. int saveCount = c->getSaveCount();
  248. SkPaint p;
  249. c->saveLayer(nullptr, &p);
  250. c->restore();
  251. REPORTER_ASSERT(r, c->getSaveCount() == saveCount);
  252. },
  253. [](SkCanvas* c, skiatest::Reporter* r) {
  254. // This test exercises a functionality in SkPicture that leads to the
  255. // recording of restore offset placeholders. This test will trigger an
  256. // assertion at playback time if the placeholders are not properly
  257. // filled when the recording ends.
  258. c->clipRect(kRect);
  259. c->clipRegion(make_region_from_irect(SkIRect{0, 0, 2, 1}));
  260. },
  261. [](SkCanvas* c, skiatest::Reporter* r) {
  262. // exercise fix for http://code.google.com/p/skia/issues/detail?id=560
  263. // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
  264. SkPaint paint;
  265. paint.setStrokeWidth(SkIntToScalar(1));
  266. paint.setStyle(SkPaint::kStroke_Style);
  267. SkPath path;
  268. path.moveTo(SkPoint{ 0, 0 });
  269. path.lineTo(SkPoint{ 0, SK_ScalarNearlyZero });
  270. path.lineTo(SkPoint{ SkIntToScalar(1), 0 });
  271. path.lineTo(SkPoint{ SkIntToScalar(1), SK_ScalarNearlyZero/2 });
  272. // test nearly zero length path
  273. c->drawPath(path, paint);
  274. },
  275. [](SkCanvas* c, skiatest::Reporter* r) {
  276. SkPictureRecorder recorder;
  277. SkCanvas* testCanvas = recorder.beginRecording(
  278. SkIntToScalar(kWidth), SkIntToScalar(kHeight), nullptr, 0);
  279. testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
  280. testCanvas->clipRect(kRect);
  281. testCanvas->drawRect(kRect, SkPaint());
  282. c->drawPicture(recorder.finishRecordingAsPicture());
  283. },
  284. [](SkCanvas* c, skiatest::Reporter* r) {
  285. int baseSaveCount = c->getSaveCount();
  286. int n = c->save();
  287. REPORTER_ASSERT(r, baseSaveCount == n);
  288. REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
  289. c->save();
  290. c->save();
  291. REPORTER_ASSERT(r, baseSaveCount + 3 == c->getSaveCount());
  292. c->restoreToCount(baseSaveCount + 1);
  293. REPORTER_ASSERT(r, baseSaveCount + 1 == c->getSaveCount());
  294. // should this pin to 1, or be a no-op, or crash?
  295. c->restoreToCount(0);
  296. REPORTER_ASSERT(r, 1 == c->getSaveCount());
  297. },
  298. [](SkCanvas* c, skiatest::Reporter* r) {
  299. // This test step challenges the TestDeferredCanvasStateConsistency
  300. // test cases because the opaque paint can trigger an optimization
  301. // that discards previously recorded commands. The challenge is to maintain
  302. // correct clip and matrix stack state.
  303. c->resetMatrix();
  304. c->rotate(SkIntToScalar(30));
  305. c->save();
  306. c->translate(SkIntToScalar(2), SkIntToScalar(1));
  307. c->save();
  308. c->scale(SkIntToScalar(3), SkIntToScalar(3));
  309. SkPaint paint;
  310. paint.setColor(0xFFFFFFFF);
  311. c->drawPaint(paint);
  312. c->restore();
  313. c->restore();
  314. },
  315. [](SkCanvas* c, skiatest::Reporter* r) {
  316. // This test step challenges the TestDeferredCanvasStateConsistency
  317. // test case because the canvas flush on a deferred canvas will
  318. // reset the recording session. The challenge is to maintain correct
  319. // clip and matrix stack state on the playback canvas.
  320. c->resetMatrix();
  321. c->rotate(SkIntToScalar(30));
  322. c->save();
  323. c->translate(SkIntToScalar(2), SkIntToScalar(1));
  324. c->save();
  325. c->scale(SkIntToScalar(3), SkIntToScalar(3));
  326. c->drawRect(kRect, SkPaint());
  327. c->flush();
  328. c->restore();
  329. c->restore();
  330. },
  331. [](SkCanvas* c, skiatest::Reporter* r) {
  332. SkPoint pts[4];
  333. pts[0].set(0, 0);
  334. pts[1].set(SkIntToScalar(kWidth), 0);
  335. pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
  336. pts[3].set(0, SkIntToScalar(kHeight));
  337. SkPaint paint;
  338. SkBitmap bitmap(make_n32_bitmap(kWidth, kHeight, 0x05060708));
  339. paint.setShader(bitmap.makeShader());
  340. c->drawVertices(
  341. SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, nullptr),
  342. SkBlendMode::kModulate, paint);
  343. }
  344. };
  345. DEF_TEST(Canvas_bitmap, reporter) {
  346. for (const CanvasTest& test : kCanvasTests) {
  347. SkBitmap referenceStore = make_n32_bitmap(kWidth, kHeight);
  348. SkCanvas referenceCanvas(referenceStore);
  349. test(&referenceCanvas, reporter);
  350. }
  351. }
  352. DEF_TEST(Canvas_pdf, reporter) {
  353. for (const CanvasTest& test : kCanvasTests) {
  354. SkNullWStream outStream;
  355. if (auto doc = SkPDF::MakeDocument(&outStream)) {
  356. SkCanvas* canvas = doc->beginPage(SkIntToScalar(kWidth),
  357. SkIntToScalar(kHeight));
  358. REPORTER_ASSERT(reporter, canvas);
  359. test(canvas, reporter);
  360. }
  361. }
  362. }
  363. DEF_TEST(Canvas_SaveState, reporter) {
  364. SkCanvas canvas(10, 10);
  365. REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
  366. int n = canvas.save();
  367. REPORTER_ASSERT(reporter, 1 == n);
  368. REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
  369. n = canvas.saveLayer(nullptr, nullptr);
  370. REPORTER_ASSERT(reporter, 2 == n);
  371. REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
  372. canvas.restore();
  373. REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
  374. canvas.restore();
  375. REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
  376. }
  377. DEF_TEST(Canvas_ClipEmptyPath, reporter) {
  378. SkCanvas canvas(10, 10);
  379. canvas.save();
  380. SkPath path;
  381. canvas.clipPath(path);
  382. canvas.restore();
  383. canvas.save();
  384. path.moveTo(5, 5);
  385. canvas.clipPath(path);
  386. canvas.restore();
  387. canvas.save();
  388. path.moveTo(7, 7);
  389. canvas.clipPath(path); // should not assert here
  390. canvas.restore();
  391. }
  392. namespace {
  393. class MockFilterCanvas : public SkPaintFilterCanvas {
  394. public:
  395. MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
  396. protected:
  397. bool onFilter(SkPaint&) const override { return true; }
  398. private:
  399. typedef SkPaintFilterCanvas INHERITED;
  400. };
  401. } // anonymous namespace
  402. // SkPaintFilterCanvas should inherit the initial target canvas state.
  403. DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) {
  404. SkCanvas canvas(100, 100);
  405. canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75));
  406. canvas.scale(0.5f, 0.75f);
  407. MockFilterCanvas filterCanvas(&canvas);
  408. REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
  409. REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
  410. filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100));
  411. filterCanvas.scale(0.75f, 0.5f);
  412. REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
  413. REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
  414. }
  415. ///////////////////////////////////////////////////////////////////////////////////////////////////
  416. namespace {
  417. // Subclass that takes a bool*, which it updates in its construct (true) and destructor (false)
  418. // to allow the caller to know how long the object is alive.
  419. class LifeLineCanvas : public SkCanvas {
  420. bool* fLifeLine;
  421. public:
  422. LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
  423. *fLifeLine = true;
  424. }
  425. ~LifeLineCanvas() {
  426. *fLifeLine = false;
  427. }
  428. };
  429. }
  430. // Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
  431. DEF_TEST(NWayCanvas, r) {
  432. const int w = 10;
  433. const int h = 10;
  434. bool life[2];
  435. {
  436. LifeLineCanvas c0(w, h, &life[0]);
  437. REPORTER_ASSERT(r, life[0]);
  438. }
  439. REPORTER_ASSERT(r, !life[0]);
  440. std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
  441. std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
  442. REPORTER_ASSERT(r, life[0]);
  443. REPORTER_ASSERT(r, life[1]);
  444. {
  445. SkNWayCanvas nway(w, h);
  446. nway.addCanvas(c0.get());
  447. nway.addCanvas(c1.get());
  448. REPORTER_ASSERT(r, life[0]);
  449. REPORTER_ASSERT(r, life[1]);
  450. }
  451. // Now assert that the death of the nway has NOT also killed the sub-canvases
  452. REPORTER_ASSERT(r, life[0]);
  453. REPORTER_ASSERT(r, life[1]);
  454. }
  455. // Check that CanvasStack DOES manage the lifetime of its sub-canvases
  456. DEF_TEST(CanvasStack, r) {
  457. const int w = 10;
  458. const int h = 10;
  459. bool life[2];
  460. std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
  461. std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
  462. REPORTER_ASSERT(r, life[0]);
  463. REPORTER_ASSERT(r, life[1]);
  464. {
  465. SkCanvasStack stack(w, h);
  466. stack.pushCanvas(std::move(c0), {0,0});
  467. stack.pushCanvas(std::move(c1), {0,0});
  468. REPORTER_ASSERT(r, life[0]);
  469. REPORTER_ASSERT(r, life[1]);
  470. }
  471. // Now assert that the death of the canvasstack has also killed the sub-canvases
  472. REPORTER_ASSERT(r, !life[0]);
  473. REPORTER_ASSERT(r, !life[1]);
  474. }
  475. static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
  476. REPORTER_ASSERT(r, !canvas->isClipEmpty());
  477. REPORTER_ASSERT(r, canvas->isClipRect());
  478. canvas->save();
  479. canvas->clipRect({0, 0, 0, 0});
  480. REPORTER_ASSERT(r, canvas->isClipEmpty());
  481. REPORTER_ASSERT(r, !canvas->isClipRect());
  482. canvas->restore();
  483. canvas->save();
  484. canvas->clipRect({2, 2, 6, 6});
  485. REPORTER_ASSERT(r, !canvas->isClipEmpty());
  486. REPORTER_ASSERT(r, canvas->isClipRect());
  487. canvas->restore();
  488. canvas->save();
  489. canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference); // punch a hole in the clip
  490. REPORTER_ASSERT(r, !canvas->isClipEmpty());
  491. REPORTER_ASSERT(r, !canvas->isClipRect());
  492. canvas->restore();
  493. REPORTER_ASSERT(r, !canvas->isClipEmpty());
  494. REPORTER_ASSERT(r, canvas->isClipRect());
  495. }
  496. DEF_TEST(CanvasClipType, r) {
  497. // test rasterclip backend
  498. test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
  499. // test clipstack backend
  500. SkDynamicMemoryWStream stream;
  501. if (auto doc = SkPDF::MakeDocument(&stream)) {
  502. test_cliptype(doc->beginPage(100, 100), r);
  503. }
  504. }
  505. #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
  506. DEF_TEST(Canvas_LegacyColorBehavior, r) {
  507. sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
  508. SkNamedGamut::kAdobeRGB);
  509. // Make a Adobe RGB bitmap.
  510. SkBitmap bitmap;
  511. bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs));
  512. bitmap.eraseColor(0xFF000000);
  513. // Wrap it in a legacy canvas. Test that the canvas behaves like a legacy canvas.
  514. SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy);
  515. REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace());
  516. SkPaint p;
  517. p.setColor(SK_ColorRED);
  518. canvas.drawIRect(SkIRect::MakeWH(1, 1), p);
  519. REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0)));
  520. }
  521. #endif
  522. namespace {
  523. class ZeroBoundsImageFilter : public SkImageFilter {
  524. public:
  525. static sk_sp<SkImageFilter> Make() { return sk_sp<SkImageFilter>(new ZeroBoundsImageFilter); }
  526. protected:
  527. sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage*, const Context&, SkIPoint*) const override {
  528. return nullptr;
  529. }
  530. SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&,
  531. MapDirection, const SkIRect* inputRect) const override {
  532. return SkIRect::MakeEmpty();
  533. }
  534. private:
  535. SK_FLATTENABLE_HOOKS(ZeroBoundsImageFilter)
  536. ZeroBoundsImageFilter() : INHERITED(nullptr, 0, nullptr) {}
  537. typedef SkImageFilter INHERITED;
  538. };
  539. sk_sp<SkFlattenable> ZeroBoundsImageFilter::CreateProc(SkReadBuffer& buffer) {
  540. SkDEBUGFAIL("Should never get here");
  541. return nullptr;
  542. }
  543. } // anonymous namespace
  544. DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter, r) {
  545. SkCanvas canvas(10, 10);
  546. SkPaint p;
  547. p.setImageFilter(ZeroBoundsImageFilter::Make());
  548. // This should not fail any assert.
  549. canvas.saveLayer(nullptr, &p);
  550. REPORTER_ASSERT(r, canvas.getDeviceClipBounds().isEmpty());
  551. canvas.restore();
  552. }
  553. // Test that we don't crash/assert when building a canvas with degenerate coordintes
  554. // (esp. big ones, that might invoke tiling).
  555. DEF_TEST(Canvas_degenerate_dimension, reporter) {
  556. // Need a paint that will sneak us past the quickReject in SkCanvas, so we can test the
  557. // raster code further downstream.
  558. SkPaint paint;
  559. paint.setImageFilter(SkPaintImageFilter::Make(SkPaint(), nullptr));
  560. REPORTER_ASSERT(reporter, !paint.canComputeFastBounds());
  561. const int big = 100 * 1024; // big enough to definitely trigger tiling
  562. const SkISize sizes[] {SkISize{0, big}, {big, 0}, {0, 0}};
  563. for (SkISize size : sizes) {
  564. SkBitmap bm;
  565. bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height()));
  566. SkCanvas canvas(bm);
  567. canvas.drawRect({0, 0, 100, 90*1024}, paint);
  568. }
  569. }
  570. DEF_TEST(Canvas_ClippedOutImageFilter, reporter) {
  571. SkCanvas canvas(100, 100);
  572. SkPaint p;
  573. p.setColor(SK_ColorGREEN);
  574. p.setImageFilter(SkBlurImageFilter::Make(3.0f, 3.0f, nullptr, nullptr));
  575. SkRect blurredRect = SkRect::MakeXYWH(60, 10, 30, 30);
  576. SkMatrix invM;
  577. invM.setRotate(-45);
  578. invM.mapRect(&blurredRect);
  579. const SkRect clipRect = SkRect::MakeXYWH(0, 50, 50, 50);
  580. canvas.clipRect(clipRect);
  581. canvas.rotate(45);
  582. const SkMatrix preCTM = canvas.getTotalMatrix();
  583. canvas.drawRect(blurredRect, p);
  584. const SkMatrix postCTM = canvas.getTotalMatrix();
  585. REPORTER_ASSERT(reporter, preCTM == postCTM);
  586. }