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