1 /* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 /* Description: 9 * This test defines a series of elementatry test steps that perform 10 * a single or a small group of canvas API calls. Each test step is 11 * used in several test cases that verify that different types of SkCanvas 12 * flavors and derivatives pass it and yield consistent behavior. The 13 * test cases analyse results that are queryable through the API. They do 14 * not look at rendering results. 15 * 16 * Adding test stepss: 17 * The general pattern for creating a new test step is to write a test 18 * function of the form: 19 * 20 * static void MyTestStepFunction(SkCanvas* canvas, 21 * const TestData& d, 22 * skiatest::Reporter* reporter, 23 * CanvasTestStep* testStep) 24 * { 25 * canvas->someCanvasAPImethod(); 26 * (...) 27 * REPORTER_ASSERT(reporter, (...), \ 28 * testStep->assertMessage()); 29 * } 30 * 31 * The definition of the test step function should be followed by an 32 * invocation of the TEST_STEP macro, which generates a class and 33 * instance for the test step: 34 * 35 * TEST_STEP(MyTestStep, MyTestStepFunction) 36 * 37 * There are also short hand macros for defining simple test steps 38 * in a single line of code. A simple test step is a one that is made 39 * of a single canvas API call. 40 * 41 * SIMPLE_TEST_STEP(MytestStep, someCanvasAPIMethod()); 42 * 43 * There is another macro called SIMPLE_TEST_STEP_WITH_ASSERT that 44 * works the same way as SIMPLE_TEST_STEP, and additionally verifies 45 * that the invoked method returns a non-zero value. 46 */ 47 48 #include "SkBitmap.h" 49 #include "SkCanvas.h" 50 #include "SkClipStack.h" 51 #include "SkDocument.h" 52 #include "SkMatrix.h" 53 #include "SkNWayCanvas.h" 54 #include "SkPaint.h" 55 #include "SkPaintFilterCanvas.h" 56 #include "SkPath.h" 57 #include "SkPicture.h" 58 #include "SkPictureRecord.h" 59 #include "SkPictureRecorder.h" 60 #include "SkRasterClip.h" 61 #include "SkRect.h" 62 #include "SkRegion.h" 63 #include "SkShader.h" 64 #include "SkSpecialImage.h" 65 #include "SkStream.h" 66 #include "SkSurface.h" 67 #include "SkTDArray.h" 68 #include "SkTemplates.h" 69 #include "SkVertices.h" 70 #include "Test.h" 71 72 DEF_TEST(canvas_clipbounds, reporter) { 73 SkCanvas canvas(10, 10); 74 SkIRect irect, irect2; 75 SkRect rect, rect2; 76 77 irect = canvas.getDeviceClipBounds(); 78 REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10)); 79 REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2)); 80 REPORTER_ASSERT(reporter, irect == irect2); 81 82 // local bounds are always too big today -- can we trim them? 83 rect = canvas.getLocalClipBounds(); 84 REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10))); 85 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2)); 86 REPORTER_ASSERT(reporter, rect == rect2); 87 88 canvas.clipRect(SkRect::MakeEmpty()); 89 90 irect = canvas.getDeviceClipBounds(); 91 REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty()); 92 REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2)); 93 REPORTER_ASSERT(reporter, irect == irect2); 94 95 rect = canvas.getLocalClipBounds(); 96 REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty()); 97 REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2)); 98 REPORTER_ASSERT(reporter, rect == rect2); 99 100 // Test for wacky sizes that we (historically) have guarded against 101 { 102 SkCanvas c(-10, -20); 103 REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty()); 104 105 SkPictureRecorder().beginRecording({ 5, 5, 4, 4 }); 106 } 107 } 108 109 // Will call proc with multiple styles of canvas (recording, raster, pdf) 110 template <typename F> static void multi_canvas_driver(int w, int h, F proc) { 111 proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h))); 112 113 SkNullWStream stream; 114 proc(SkDocument::MakePDF(&stream)->beginPage(SkIntToScalar(w), SkIntToScalar(h))); 115 116 proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas()); 117 } 118 119 const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 }; 120 121 static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) { 122 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR); 123 124 const SkIRect restrictionR = { 2, 2, 8, 8 }; 125 canvas->androidFramework_setDeviceClipRestriction(restrictionR); 126 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR); 127 128 const SkIRect clipR = { 4, 4, 6, 6 }; 129 canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect); 130 REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR); 131 132 #ifdef SK_SUPPORT_DEPRECATED_CLIPOPS 133 // now test that expanding clipops can't exceed the restriction 134 const SkClipOp expanders[] = { 135 SkClipOp::kUnion_deprecated, 136 SkClipOp::kXOR_deprecated, 137 SkClipOp::kReverseDifference_deprecated, 138 SkClipOp::kReplace_deprecated, 139 }; 140 141 const SkRect expandR = { 0, 0, 5, 9 }; 142 SkASSERT(!SkRect::Make(restrictionR).contains(expandR)); 143 144 for (SkClipOp op : expanders) { 145 canvas->save(); 146 canvas->clipRect(expandR, op); 147 REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds())); 148 canvas->restore(); 149 } 150 #endif 151 } 152 153 /** 154 * Clip restriction logic exists in the canvas itself, and in various kinds of devices. 155 * 156 * This test explicitly tries to exercise that variety: 157 * - picture : empty device but exercises canvas itself 158 * - pdf : uses SkClipStack in its device (as does SVG and GPU) 159 * - raster : uses SkRasterClip in its device 160 */ 161 DEF_TEST(canvas_clip_restriction, reporter) { 162 multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(), 163 [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); }); 164 } 165 166 DEF_TEST(canvas_empty_clip, reporter) { 167 multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) { 168 canvas->save(); 169 canvas->clipRect({0, 0, 20, 40 }); 170 REPORTER_ASSERT(reporter, !canvas->isClipEmpty()); 171 canvas->clipRect({30, 0, 50, 40 }); 172 REPORTER_ASSERT(reporter, canvas->isClipEmpty()); 173 }); 174 } 175 176 static const int kWidth = 2, kHeight = 2; 177 178 static void createBitmap(SkBitmap* bm, SkColor color) { 179 bm->allocN32Pixels(kWidth, kHeight); 180 bm->eraseColor(color); 181 } 182 183 /////////////////////////////////////////////////////////////////////////////// 184 // Constants used by test steps 185 const SkPoint kTestPoints[] = { 186 {SkIntToScalar(0), SkIntToScalar(0)}, 187 {SkIntToScalar(2), SkIntToScalar(1)}, 188 {SkIntToScalar(0), SkIntToScalar(2)} 189 }; 190 const SkPoint kTestPoints2[] = { 191 { SkIntToScalar(0), SkIntToScalar(1) }, 192 { SkIntToScalar(1), SkIntToScalar(1) }, 193 { SkIntToScalar(2), SkIntToScalar(1) }, 194 { SkIntToScalar(3), SkIntToScalar(1) }, 195 { SkIntToScalar(4), SkIntToScalar(1) }, 196 { SkIntToScalar(5), SkIntToScalar(1) }, 197 { SkIntToScalar(6), SkIntToScalar(1) }, 198 { SkIntToScalar(7), SkIntToScalar(1) }, 199 { SkIntToScalar(8), SkIntToScalar(1) }, 200 { SkIntToScalar(9), SkIntToScalar(1) }, 201 { SkIntToScalar(10), SkIntToScalar(1) } 202 }; 203 204 struct TestData { 205 public: 206 TestData() 207 : fRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), 208 SkIntToScalar(2), SkIntToScalar(1))) 209 , fMatrix(TestMatrix()) 210 , fPath(TestPath()) 211 , fNearlyZeroLengthPath(TestNearlyZeroLengthPath()) 212 , fIRect(SkIRect::MakeXYWH(0, 0, 2, 1)) 213 , fRegion(TestRegion()) 214 , fColor(0x01020304) 215 , fPoints(kTestPoints) 216 , fPointCount(3) 217 , fWidth(2) 218 , fHeight(2) 219 , fText("Hello World") 220 , fPoints2(kTestPoints2) 221 , fBitmap(TestBitmap()) 222 { } 223 224 SkRect fRect; 225 SkMatrix fMatrix; 226 SkPath fPath; 227 SkPath fNearlyZeroLengthPath; 228 SkIRect fIRect; 229 SkRegion fRegion; 230 SkColor fColor; 231 SkPaint fPaint; 232 const SkPoint* fPoints; 233 size_t fPointCount; 234 int fWidth; 235 int fHeight; 236 SkString fText; 237 const SkPoint* fPoints2; 238 SkBitmap fBitmap; 239 240 private: 241 static SkMatrix TestMatrix() { 242 SkMatrix matrix; 243 matrix.reset(); 244 matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); 245 246 return matrix; 247 } 248 static SkPath TestPath() { 249 SkPath path; 250 path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), 251 SkIntToScalar(2), SkIntToScalar(1))); 252 return path; 253 } 254 static SkPath TestNearlyZeroLengthPath() { 255 SkPath path; 256 SkPoint pt1 = { 0, 0 }; 257 SkPoint pt2 = { 0, SK_ScalarNearlyZero }; 258 SkPoint pt3 = { SkIntToScalar(1), 0 }; 259 SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 }; 260 path.moveTo(pt1); 261 path.lineTo(pt2); 262 path.lineTo(pt3); 263 path.lineTo(pt4); 264 return path; 265 } 266 static SkRegion TestRegion() { 267 SkRegion region; 268 SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1); 269 region.setRect(rect); 270 return region; 271 } 272 static SkBitmap TestBitmap() { 273 SkBitmap bitmap; 274 createBitmap(&bitmap, 0x05060708); 275 return bitmap; 276 } 277 }; 278 279 // Format strings that describe the test context. The %s token is where 280 // the name of the test step is inserted. The context is required for 281 // disambiguating the error in the case of failures that are reported in 282 // functions that are called multiple times in different contexts (test 283 // cases and test steps). 284 static const char* const kDefaultAssertMessageFormat = "%s"; 285 static const char* const kCanvasDrawAssertMessageFormat = 286 "Drawing test step %s with SkCanvas"; 287 static const char* const kPdfAssertMessageFormat = 288 "PDF sanity check failed %s"; 289 290 class CanvasTestStep; 291 static SkTDArray<CanvasTestStep*>& testStepArray() { 292 static SkTDArray<CanvasTestStep*> theTests; 293 return theTests; 294 } 295 296 class CanvasTestStep { 297 public: 298 CanvasTestStep(bool fEnablePdfTesting = true) { 299 *testStepArray().append() = this; 300 fAssertMessageFormat = kDefaultAssertMessageFormat; 301 this->fEnablePdfTesting = fEnablePdfTesting; 302 } 303 virtual ~CanvasTestStep() { } 304 305 virtual void draw(SkCanvas*, const TestData&, skiatest::Reporter*) = 0; 306 virtual const char* name() const = 0; 307 308 const char* assertMessage() { 309 fAssertMessage.printf(fAssertMessageFormat, name()); 310 return fAssertMessage.c_str(); 311 } 312 313 void setAssertMessageFormat(const char* format) { 314 fAssertMessageFormat = format; 315 } 316 317 bool enablePdfTesting() { return fEnablePdfTesting; } 318 319 private: 320 SkString fAssertMessage; 321 const char* fAssertMessageFormat; 322 bool fEnablePdfTesting; 323 }; 324 325 /////////////////////////////////////////////////////////////////////////////// 326 // Macros for defining test steps 327 328 #define TEST_STEP(NAME, FUNCTION) \ 329 class NAME##_TestStep : public CanvasTestStep{ \ 330 public: \ 331 virtual void draw(SkCanvas* canvas, const TestData& d, \ 332 skiatest::Reporter* reporter) { \ 333 FUNCTION (canvas, d, reporter, this); \ 334 } \ 335 virtual const char* name() const {return #NAME ;} \ 336 }; \ 337 static NAME##_TestStep NAME##_TestStepInstance; 338 339 #define TEST_STEP_NO_PDF(NAME, FUNCTION) \ 340 class NAME##_TestStep : public CanvasTestStep{ \ 341 public: \ 342 NAME##_TestStep() : CanvasTestStep(false) {} \ 343 virtual void draw(SkCanvas* canvas, const TestData& d, \ 344 skiatest::Reporter* reporter) { \ 345 FUNCTION (canvas, d, reporter, this); \ 346 } \ 347 virtual const char* name() const {return #NAME ;} \ 348 }; \ 349 static NAME##_TestStep NAME##_TestStepInstance; 350 351 #define SIMPLE_TEST_STEP(NAME, CALL) \ 352 static void NAME##TestStep(SkCanvas* canvas, const TestData& d, \ 353 skiatest::Reporter*, CanvasTestStep*) { \ 354 canvas-> CALL ; \ 355 } \ 356 TEST_STEP(NAME, NAME##TestStep ) 357 358 #define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL) \ 359 static void NAME##TestStep(SkCanvas* canvas, const TestData& d, skiatest::Reporter*, \ 360 CanvasTestStep* testStep) { \ 361 REPORTER_ASSERT(reporter, canvas->CALL, testStep->assertMessage()); \ 362 } \ 363 TEST_STEP(NAME, NAME##TestStep) 364 365 /////////////////////////////////////////////////////////////////////////////// 366 // Basic test steps for most virtual methods in SkCanvas that draw or affect 367 // the state of the canvas. 368 369 SIMPLE_TEST_STEP(Translate, translate(SkIntToScalar(1), SkIntToScalar(2))); 370 SIMPLE_TEST_STEP(Scale, scale(SkIntToScalar(1), SkIntToScalar(2))); 371 SIMPLE_TEST_STEP(Rotate, rotate(SkIntToScalar(1))); 372 SIMPLE_TEST_STEP(Skew, skew(SkIntToScalar(1), SkIntToScalar(2))); 373 SIMPLE_TEST_STEP(Concat, concat(d.fMatrix)); 374 SIMPLE_TEST_STEP(SetMatrix, setMatrix(d.fMatrix)); 375 SIMPLE_TEST_STEP(ClipRect, clipRect(d.fRect)); 376 SIMPLE_TEST_STEP(ClipPath, clipPath(d.fPath)); 377 SIMPLE_TEST_STEP(ClipRegion, clipRegion(d.fRegion, kReplace_SkClipOp)); 378 SIMPLE_TEST_STEP(Clear, clear(d.fColor)); 379 380 /////////////////////////////////////////////////////////////////////////////// 381 // Complex test steps 382 383 static void SaveMatrixClipStep(SkCanvas* canvas, const TestData& d, 384 skiatest::Reporter* reporter, CanvasTestStep* testStep) { 385 int saveCount = canvas->getSaveCount(); 386 canvas->save(); 387 canvas->translate(SkIntToScalar(1), SkIntToScalar(2)); 388 canvas->clipRegion(d.fRegion); 389 canvas->restore(); 390 REPORTER_ASSERT(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); 391 REPORTER_ASSERT(reporter, canvas->getTotalMatrix().isIdentity(), testStep->assertMessage()); 392 // REPORTER_ASSERT(reporter, canvas->getTotalClip() != kTestRegion, 393 // testStep->assertMessage()); 394 } 395 TEST_STEP(SaveMatrixClip, SaveMatrixClipStep); 396 397 static void SaveLayerStep(SkCanvas* canvas, const TestData& d, 398 skiatest::Reporter* reporter, CanvasTestStep* testStep) { 399 int saveCount = canvas->getSaveCount(); 400 canvas->saveLayer(nullptr, nullptr); 401 canvas->restore(); 402 REPORTER_ASSERT(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); 403 } 404 TEST_STEP(SaveLayer, SaveLayerStep); 405 406 static void BoundedSaveLayerStep(SkCanvas* canvas, const TestData& d, 407 skiatest::Reporter* reporter, CanvasTestStep* testStep) { 408 int saveCount = canvas->getSaveCount(); 409 canvas->saveLayer(&d.fRect, nullptr); 410 canvas->restore(); 411 REPORTER_ASSERT(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); 412 } 413 TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep); 414 415 static void PaintSaveLayerStep(SkCanvas* canvas, const TestData& d, 416 skiatest::Reporter* reporter, CanvasTestStep* testStep) { 417 int saveCount = canvas->getSaveCount(); 418 canvas->saveLayer(nullptr, &d.fPaint); 419 canvas->restore(); 420 REPORTER_ASSERT(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); 421 } 422 TEST_STEP(PaintSaveLayer, PaintSaveLayerStep); 423 424 static void TwoClipOpsStep(SkCanvas* canvas, const TestData& d, 425 skiatest::Reporter*, CanvasTestStep*) { 426 // This test exercises a functionality in SkPicture that leads to the 427 // recording of restore offset placeholders. This test will trigger an 428 // assertion at playback time if the placeholders are not properly 429 // filled when the recording ends. 430 canvas->clipRect(d.fRect); 431 canvas->clipRegion(d.fRegion); 432 } 433 TEST_STEP(TwoClipOps, TwoClipOpsStep); 434 435 // exercise fix for http://code.google.com/p/skia/issues/detail?id=560 436 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero') 437 static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, const TestData& d, 438 skiatest::Reporter*, CanvasTestStep*) { 439 SkPaint paint; 440 paint.setStrokeWidth(SkIntToScalar(1)); 441 paint.setStyle(SkPaint::kStroke_Style); 442 443 canvas->drawPath(d.fNearlyZeroLengthPath, paint); 444 } 445 TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep); 446 447 static void DrawVerticesShaderTestStep(SkCanvas* canvas, const TestData& d, 448 skiatest::Reporter*, CanvasTestStep*) { 449 SkPoint pts[4]; 450 pts[0].set(0, 0); 451 pts[1].set(SkIntToScalar(d.fWidth), 0); 452 pts[2].set(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight)); 453 pts[3].set(0, SkIntToScalar(d.fHeight)); 454 SkPaint paint; 455 paint.setShader(SkShader::MakeBitmapShader(d.fBitmap, SkShader::kClamp_TileMode, 456 SkShader::kClamp_TileMode)); 457 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, 458 nullptr), 459 SkBlendMode::kModulate, paint); 460 } 461 // NYI: issue 240. 462 TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep); 463 464 static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d, 465 skiatest::Reporter*, CanvasTestStep*) { 466 SkPictureRecorder recorder; 467 SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight), 468 nullptr, 0); 469 testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1)); 470 testCanvas->clipRect(d.fRect); 471 testCanvas->drawRect(d.fRect, d.fPaint); 472 473 canvas->drawPicture(recorder.finishRecordingAsPicture()); 474 } 475 TEST_STEP(DrawPicture, DrawPictureTestStep); 476 477 static void SaveRestoreTestStep(SkCanvas* canvas, const TestData& d, 478 skiatest::Reporter* reporter, CanvasTestStep* testStep) { 479 int baseSaveCount = canvas->getSaveCount(); 480 int n = canvas->save(); 481 REPORTER_ASSERT(reporter, baseSaveCount == n, testStep->assertMessage()); 482 REPORTER_ASSERT(reporter, baseSaveCount + 1 == canvas->getSaveCount(), 483 testStep->assertMessage()); 484 canvas->save(); 485 canvas->save(); 486 REPORTER_ASSERT(reporter, baseSaveCount + 3 == canvas->getSaveCount(), 487 testStep->assertMessage()); 488 canvas->restoreToCount(baseSaveCount + 1); 489 REPORTER_ASSERT(reporter, baseSaveCount + 1 == canvas->getSaveCount(), 490 testStep->assertMessage()); 491 492 // should this pin to 1, or be a no-op, or crash? 493 canvas->restoreToCount(0); 494 REPORTER_ASSERT(reporter, 1 == canvas->getSaveCount(), testStep->assertMessage()); 495 } 496 TEST_STEP(SaveRestore, SaveRestoreTestStep); 497 498 static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, const TestData& d, 499 skiatest::Reporter*, CanvasTestStep*) { 500 // This test step challenges the TestDeferredCanvasStateConsistency 501 // test cases because the opaque paint can trigger an optimization 502 // that discards previously recorded commands. The challenge is to maintain 503 // correct clip and matrix stack state. 504 canvas->resetMatrix(); 505 canvas->rotate(SkIntToScalar(30)); 506 canvas->save(); 507 canvas->translate(SkIntToScalar(2), SkIntToScalar(1)); 508 canvas->save(); 509 canvas->scale(SkIntToScalar(3), SkIntToScalar(3)); 510 SkPaint paint; 511 paint.setColor(0xFFFFFFFF); 512 canvas->drawPaint(paint); 513 canvas->restore(); 514 canvas->restore(); 515 } 516 TEST_STEP(NestedSaveRestoreWithSolidPaint, \ 517 NestedSaveRestoreWithSolidPaintTestStep); 518 519 static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, const TestData& d, 520 skiatest::Reporter*, CanvasTestStep*) { 521 // This test step challenges the TestDeferredCanvasStateConsistency 522 // test case because the canvas flush on a deferred canvas will 523 // reset the recording session. The challenge is to maintain correct 524 // clip and matrix stack state on the playback canvas. 525 canvas->resetMatrix(); 526 canvas->rotate(SkIntToScalar(30)); 527 canvas->save(); 528 canvas->translate(SkIntToScalar(2), SkIntToScalar(1)); 529 canvas->save(); 530 canvas->scale(SkIntToScalar(3), SkIntToScalar(3)); 531 canvas->drawRect(d.fRect,d.fPaint); 532 canvas->flush(); 533 canvas->restore(); 534 canvas->restore(); 535 } 536 TEST_STEP(NestedSaveRestoreWithFlush, NestedSaveRestoreWithFlushTestStep); 537 538 static void TestPdfDevice(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* step) { 539 SkDynamicMemoryWStream outStream; 540 sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream)); 541 REPORTER_ASSERT(reporter, doc); 542 if (!doc) { 543 return; 544 } 545 SkCanvas* canvas = doc->beginPage(SkIntToScalar(d.fWidth), 546 SkIntToScalar(d.fHeight)); 547 REPORTER_ASSERT(reporter, canvas); 548 step->setAssertMessageFormat(kPdfAssertMessageFormat); 549 step->draw(canvas, d, reporter); 550 } 551 552 /* 553 * This sub-test verifies that the test step passes when executed 554 * with SkCanvas and with classes derrived from SkCanvas. It also verifies 555 * that the all canvas derivatives report the same state as an SkCanvas 556 * after having executed the test step. 557 */ 558 static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const TestData& d, 559 CanvasTestStep* testStep) { 560 SkBitmap referenceStore; 561 createBitmap(&referenceStore, 0xFFFFFFFF); 562 SkCanvas referenceCanvas(referenceStore); 563 testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat); 564 testStep->draw(&referenceCanvas, d, reporter); 565 } 566 567 static void test_newraster(skiatest::Reporter* reporter) { 568 SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); 569 const size_t minRowBytes = info.minRowBytes(); 570 const size_t size = info.computeByteSize(minRowBytes); 571 SkAutoTMalloc<SkPMColor> storage(size); 572 SkPMColor* baseAddr = storage.get(); 573 sk_bzero(baseAddr, size); 574 575 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); 576 REPORTER_ASSERT(reporter, canvas); 577 578 SkPixmap pmap; 579 const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr; 580 REPORTER_ASSERT(reporter, addr); 581 REPORTER_ASSERT(reporter, info == pmap.info()); 582 REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes()); 583 for (int y = 0; y < info.height(); ++y) { 584 for (int x = 0; x < info.width(); ++x) { 585 REPORTER_ASSERT(reporter, 0 == addr[x]); 586 } 587 addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes()); 588 } 589 590 // now try a deliberately bad info 591 info = info.makeWH(-1, info.height()); 592 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); 593 594 // too big 595 info = info.makeWH(1 << 30, 1 << 30); 596 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); 597 598 // not a valid pixel type 599 info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType()); 600 REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); 601 602 // We should succeed with a zero-sized valid info 603 info = SkImageInfo::MakeN32Premul(0, 0); 604 canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); 605 REPORTER_ASSERT(reporter, canvas); 606 } 607 608 DEF_TEST(Canvas, reporter) { 609 TestData d; 610 611 for (int testStep = 0; testStep < testStepArray().count(); testStep++) { 612 TestOverrideStateConsistency(reporter, d, testStepArray()[testStep]); 613 if (testStepArray()[testStep]->enablePdfTesting()) { 614 TestPdfDevice(reporter, d, testStepArray()[testStep]); 615 } 616 } 617 618 test_newraster(reporter); 619 } 620 621 DEF_TEST(Canvas_SaveState, reporter) { 622 SkCanvas canvas(10, 10); 623 REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); 624 625 int n = canvas.save(); 626 REPORTER_ASSERT(reporter, 1 == n); 627 REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); 628 629 n = canvas.saveLayer(nullptr, nullptr); 630 REPORTER_ASSERT(reporter, 2 == n); 631 REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount()); 632 633 canvas.restore(); 634 REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); 635 canvas.restore(); 636 REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); 637 } 638 639 DEF_TEST(Canvas_ClipEmptyPath, reporter) { 640 SkCanvas canvas(10, 10); 641 canvas.save(); 642 SkPath path; 643 canvas.clipPath(path); 644 canvas.restore(); 645 canvas.save(); 646 path.moveTo(5, 5); 647 canvas.clipPath(path); 648 canvas.restore(); 649 canvas.save(); 650 path.moveTo(7, 7); 651 canvas.clipPath(path); // should not assert here 652 canvas.restore(); 653 } 654 655 namespace { 656 657 class MockFilterCanvas : public SkPaintFilterCanvas { 658 public: 659 MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { } 660 661 protected: 662 bool onFilter(SkTCopyOnFirstWrite<SkPaint>*, Type) const override { return true; } 663 664 private: 665 typedef SkPaintFilterCanvas INHERITED; 666 }; 667 668 } // anonymous namespace 669 670 // SkPaintFilterCanvas should inherit the initial target canvas state. 671 DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) { 672 SkCanvas canvas(100, 100); 673 canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75)); 674 canvas.scale(0.5f, 0.75f); 675 676 MockFilterCanvas filterCanvas(&canvas); 677 REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix()); 678 REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds()); 679 680 filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100)); 681 filterCanvas.scale(0.75f, 0.5f); 682 REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix()); 683 REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds())); 684 } 685 686 /////////////////////////////////////////////////////////////////////////////////////////////////// 687 688 #include "SkCanvasStack.h" 689 #include "SkNWayCanvas.h" 690 691 // Subclass that takes a bool*, which it updates in its construct (true) and destructor (false) 692 // to allow the caller to know how long the object is alive. 693 class LifeLineCanvas : public SkCanvas { 694 bool* fLifeLine; 695 public: 696 LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) { 697 *fLifeLine = true; 698 } 699 ~LifeLineCanvas() { 700 *fLifeLine = false; 701 } 702 }; 703 704 // Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases 705 DEF_TEST(NWayCanvas, r) { 706 const int w = 10; 707 const int h = 10; 708 bool life[2]; 709 { 710 LifeLineCanvas c0(w, h, &life[0]); 711 REPORTER_ASSERT(r, life[0]); 712 } 713 REPORTER_ASSERT(r, !life[0]); 714 715 716 std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0])); 717 std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1])); 718 REPORTER_ASSERT(r, life[0]); 719 REPORTER_ASSERT(r, life[1]); 720 721 { 722 SkNWayCanvas nway(w, h); 723 nway.addCanvas(c0.get()); 724 nway.addCanvas(c1.get()); 725 REPORTER_ASSERT(r, life[0]); 726 REPORTER_ASSERT(r, life[1]); 727 } 728 // Now assert that the death of the nway has NOT also killed the sub-canvases 729 REPORTER_ASSERT(r, life[0]); 730 REPORTER_ASSERT(r, life[1]); 731 } 732 733 // Check that CanvasStack DOES manage the lifetime of its sub-canvases 734 DEF_TEST(CanvasStack, r) { 735 const int w = 10; 736 const int h = 10; 737 bool life[2]; 738 std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0])); 739 std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1])); 740 REPORTER_ASSERT(r, life[0]); 741 REPORTER_ASSERT(r, life[1]); 742 743 { 744 SkCanvasStack stack(w, h); 745 stack.pushCanvas(std::move(c0), {0,0}); 746 stack.pushCanvas(std::move(c1), {0,0}); 747 REPORTER_ASSERT(r, life[0]); 748 REPORTER_ASSERT(r, life[1]); 749 } 750 // Now assert that the death of the canvasstack has also killed the sub-canvases 751 REPORTER_ASSERT(r, !life[0]); 752 REPORTER_ASSERT(r, !life[1]); 753 } 754 755 static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) { 756 REPORTER_ASSERT(r, !canvas->isClipEmpty()); 757 REPORTER_ASSERT(r, canvas->isClipRect()); 758 759 canvas->save(); 760 canvas->clipRect({0, 0, 0, 0}); 761 REPORTER_ASSERT(r, canvas->isClipEmpty()); 762 REPORTER_ASSERT(r, !canvas->isClipRect()); 763 canvas->restore(); 764 765 canvas->save(); 766 canvas->clipRect({2, 2, 6, 6}); 767 REPORTER_ASSERT(r, !canvas->isClipEmpty()); 768 REPORTER_ASSERT(r, canvas->isClipRect()); 769 canvas->restore(); 770 771 canvas->save(); 772 canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference); // punch a hole in the clip 773 REPORTER_ASSERT(r, !canvas->isClipEmpty()); 774 REPORTER_ASSERT(r, !canvas->isClipRect()); 775 canvas->restore(); 776 777 REPORTER_ASSERT(r, !canvas->isClipEmpty()); 778 REPORTER_ASSERT(r, canvas->isClipRect()); 779 } 780 781 DEF_TEST(CanvasClipType, r) { 782 // test rasterclip backend 783 test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r); 784 785 // test clipstack backend 786 SkDynamicMemoryWStream stream; 787 test_cliptype(SkDocument::MakePDF(&stream)->beginPage(100, 100), r); 788 } 789 790 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 791 DEF_TEST(Canvas_LegacyColorBehavior, r) { 792 sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, 793 SkColorSpace::kAdobeRGB_Gamut); 794 795 // Make a Adobe RGB bitmap. 796 SkBitmap bitmap; 797 bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs)); 798 bitmap.eraseColor(0xFF000000); 799 800 // Wrap it in a legacy canvas. Test that the canvas behaves like a legacy canvas. 801 SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy); 802 REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace()); 803 SkPaint p; 804 p.setColor(SK_ColorRED); 805 canvas.drawIRect(SkIRect::MakeWH(1, 1), p); 806 REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0))); 807 } 808 #endif 809 810 namespace { 811 812 class ZeroBoundsImageFilter : public SkImageFilter { 813 public: 814 static sk_sp<SkImageFilter> Make() { return sk_sp<SkImageFilter>(new ZeroBoundsImageFilter); } 815 816 SK_TO_STRING_OVERRIDE() 817 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(ZeroBoundsImageFilter) 818 819 protected: 820 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage*, const Context&, SkIPoint*) const override { 821 return nullptr; 822 } 823 sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { return nullptr; } 824 SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override { 825 return SkIRect::MakeEmpty(); 826 } 827 828 private: 829 ZeroBoundsImageFilter() : INHERITED(nullptr, 0, nullptr) {} 830 831 typedef SkImageFilter INHERITED; 832 }; 833 834 sk_sp<SkFlattenable> ZeroBoundsImageFilter::CreateProc(SkReadBuffer& buffer) { 835 SkDEBUGFAIL("Should never get here"); 836 return nullptr; 837 } 838 839 #ifndef SK_IGNORE_TO_STRING 840 void ZeroBoundsImageFilter::toString(SkString* str) const { 841 str->appendf("ZeroBoundsImageFilter: ()"); 842 } 843 #endif 844 845 } // anonymous namespace 846 847 DEF_TEST(Canvas_SaveLayerWithNullBoundsAndZeroBoundsImageFilter, r) { 848 SkCanvas canvas(10, 10); 849 SkPaint p; 850 p.setImageFilter(ZeroBoundsImageFilter::Make()); 851 // This should not fail any assert. 852 canvas.saveLayer(nullptr, &p); 853 REPORTER_ASSERT(r, canvas.getDeviceClipBounds().isEmpty()); 854 canvas.restore(); 855 } 856