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 #include "SkBBHFactory.h" 9 #include "SkBBoxHierarchy.h" 10 #include "SkBigPicture.h" 11 #include "SkBitmap.h" 12 #include "SkCanvas.h" 13 #include "SkClipOp.h" 14 #include "SkClipOpPriv.h" 15 #include "SkColor.h" 16 #include "SkData.h" 17 #include "SkFontStyle.h" 18 #include "SkImageInfo.h" 19 #include "SkMatrix.h" 20 #include "SkMiniRecorder.h" 21 #include "SkPaint.h" 22 #include "SkPath.h" 23 #include "SkPicturePriv.h" 24 #include "SkPictureRecorder.h" 25 #include "SkPixelRef.h" 26 #include "SkRandom.h" 27 #include "SkRect.h" 28 #include "SkRectPriv.h" 29 #include "SkRefCnt.h" 30 #include "SkScalar.h" 31 #include "SkShader.h" 32 #include "SkStream.h" 33 #include "SkTypeface.h" 34 #include "SkTypes.h" 35 #include "Test.h" 36 37 #include <memory> 38 39 class SkRRect; 40 class SkRegion; 41 template <typename T> class SkTDArray; 42 43 44 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 45 bm->allocN32Pixels(w, h); 46 bm->eraseColor(color); 47 if (immutable) { 48 bm->setImmutable(); 49 } 50 } 51 52 #ifdef SK_DEBUG 53 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire 54 // in debug mode, so only run in debug mode. 55 static void test_deleting_empty_picture() { 56 SkPictureRecorder recorder; 57 // Creates an SkPictureRecord 58 recorder.beginRecording(0, 0); 59 // Turns that into an SkPicture 60 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 61 // Ceates a new SkPictureRecord 62 recorder.beginRecording(0, 0); 63 } 64 65 // Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 66 static void test_serializing_empty_picture() { 67 SkPictureRecorder recorder; 68 recorder.beginRecording(0, 0); 69 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 70 SkDynamicMemoryWStream stream; 71 picture->serialize(&stream); 72 } 73 #endif 74 75 static void rand_op(SkCanvas* canvas, SkRandom& rand) { 76 SkPaint paint; 77 SkRect rect = SkRect::MakeWH(50, 50); 78 79 SkScalar unit = rand.nextUScalar1(); 80 if (unit <= 0.3) { 81 // SkDebugf("save\n"); 82 canvas->save(); 83 } else if (unit <= 0.6) { 84 // SkDebugf("restore\n"); 85 canvas->restore(); 86 } else if (unit <= 0.9) { 87 // SkDebugf("clip\n"); 88 canvas->clipRect(rect); 89 } else { 90 // SkDebugf("draw\n"); 91 canvas->drawPaint(paint); 92 } 93 } 94 95 static void set_canvas_to_save_count_4(SkCanvas* canvas) { 96 canvas->restoreToCount(1); 97 canvas->save(); 98 canvas->save(); 99 canvas->save(); 100 } 101 102 /** 103 * A canvas that records the number of saves, saveLayers and restores. 104 */ 105 class SaveCountingCanvas : public SkCanvas { 106 public: 107 SaveCountingCanvas(int width, int height) 108 : INHERITED(width, height) 109 , fSaveCount(0) 110 , fSaveLayerCount(0) 111 , fSaveBehindCount(0) 112 , fRestoreCount(0){ 113 } 114 115 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { 116 ++fSaveLayerCount; 117 return this->INHERITED::getSaveLayerStrategy(rec); 118 } 119 120 bool onDoSaveBehind(const SkRect* subset) override { 121 ++fSaveBehindCount; 122 return this->INHERITED::onDoSaveBehind(subset); 123 } 124 125 void willSave() override { 126 ++fSaveCount; 127 this->INHERITED::willSave(); 128 } 129 130 void willRestore() override { 131 ++fRestoreCount; 132 this->INHERITED::willRestore(); 133 } 134 135 unsigned int getSaveCount() const { return fSaveCount; } 136 unsigned int getSaveLayerCount() const { return fSaveLayerCount; } 137 unsigned int getSaveBehindCount() const { return fSaveBehindCount; } 138 unsigned int getRestoreCount() const { return fRestoreCount; } 139 140 private: 141 unsigned int fSaveCount; 142 unsigned int fSaveLayerCount; 143 unsigned int fSaveBehindCount; 144 unsigned int fRestoreCount; 145 146 typedef SkCanvas INHERITED; 147 }; 148 149 void check_save_state(skiatest::Reporter* reporter, SkPicture* picture, 150 unsigned int numSaves, unsigned int numSaveLayers, 151 unsigned int numRestores) { 152 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()), 153 SkScalarCeilToInt(picture->cullRect().height())); 154 155 picture->playback(&canvas); 156 157 // Optimizations may have removed these, 158 // so expect to have seen no more than num{Saves,SaveLayers,Restores}. 159 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount()); 160 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount()); 161 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount()); 162 } 163 164 // This class exists so SkPicture can friend it and give it access to 165 // the 'partialReplay' method. 166 class SkPictureRecorderReplayTester { 167 public: 168 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) { 169 SkPictureRecorder recorder2; 170 171 SkCanvas* canvas = recorder2.beginRecording(10, 10); 172 173 recorder->partialReplay(canvas); 174 175 return recorder2.finishRecordingAsPicture(); 176 } 177 }; 178 179 static void create_imbalance(SkCanvas* canvas) { 180 SkRect clipRect = SkRect::MakeWH(2, 2); 181 SkRect drawRect = SkRect::MakeWH(10, 10); 182 canvas->save(); 183 canvas->clipRect(clipRect, kReplace_SkClipOp); 184 canvas->translate(1.0f, 1.0f); 185 SkPaint p; 186 p.setColor(SK_ColorGREEN); 187 canvas->drawRect(drawRect, p); 188 // no restore 189 } 190 191 // This tests that replaying a potentially unbalanced picture into a canvas 192 // doesn't affect the canvas' save count or matrix/clip state. 193 static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) { 194 SkBitmap bm; 195 bm.allocN32Pixels(4, 3); 196 SkCanvas canvas(bm); 197 198 int beforeSaveCount = canvas.getSaveCount(); 199 200 SkMatrix beforeMatrix = canvas.getTotalMatrix(); 201 202 SkRect beforeClip = canvas.getLocalClipBounds(); 203 204 canvas.drawPicture(picture); 205 206 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 207 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 208 209 SkRect afterClip = canvas.getLocalClipBounds(); 210 211 REPORTER_ASSERT(reporter, afterClip == beforeClip); 212 } 213 214 // Test out SkPictureRecorder::partialReplay 215 DEF_TEST(PictureRecorder_replay, reporter) { 216 // check save/saveLayer state 217 { 218 SkPictureRecorder recorder; 219 220 SkCanvas* canvas = recorder.beginRecording(10, 10); 221 222 canvas->saveLayer(nullptr, nullptr); 223 224 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 225 226 // The extra save and restore comes from the Copy process. 227 check_save_state(reporter, copy.get(), 2, 1, 3); 228 229 canvas->saveLayer(nullptr, nullptr); 230 231 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 232 233 check_save_state(reporter, final.get(), 1, 2, 3); 234 235 // The copy shouldn't pick up any operations added after it was made 236 check_save_state(reporter, copy.get(), 2, 1, 3); 237 } 238 239 // Recreate the Android partialReplay test case 240 { 241 SkPictureRecorder recorder; 242 243 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0); 244 create_imbalance(canvas); 245 246 int expectedSaveCount = canvas->getSaveCount(); 247 248 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 249 check_balance(reporter, copy.get()); 250 251 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 252 253 // End the recording of source to test the picture finalization 254 // process isn't complicated by the partialReplay step 255 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 256 } 257 } 258 259 static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 260 SkCanvas testCanvas(100, 100); 261 set_canvas_to_save_count_4(&testCanvas); 262 263 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 264 265 SkPaint paint; 266 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 267 268 SkPictureRecorder recorder; 269 270 { 271 // Create picture with 2 unbalanced saves 272 SkCanvas* canvas = recorder.beginRecording(100, 100); 273 canvas->save(); 274 canvas->translate(10, 10); 275 canvas->drawRect(rect, paint); 276 canvas->save(); 277 canvas->translate(10, 10); 278 canvas->drawRect(rect, paint); 279 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture()); 280 281 testCanvas.drawPicture(extraSavePicture); 282 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 283 } 284 285 set_canvas_to_save_count_4(&testCanvas); 286 287 { 288 // Create picture with 2 unbalanced restores 289 SkCanvas* canvas = recorder.beginRecording(100, 100); 290 canvas->save(); 291 canvas->translate(10, 10); 292 canvas->drawRect(rect, paint); 293 canvas->save(); 294 canvas->translate(10, 10); 295 canvas->drawRect(rect, paint); 296 canvas->restore(); 297 canvas->restore(); 298 canvas->restore(); 299 canvas->restore(); 300 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture()); 301 302 testCanvas.drawPicture(extraRestorePicture); 303 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 304 } 305 306 set_canvas_to_save_count_4(&testCanvas); 307 308 { 309 SkCanvas* canvas = recorder.beginRecording(100, 100); 310 canvas->translate(10, 10); 311 canvas->drawRect(rect, paint); 312 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture()); 313 314 testCanvas.drawPicture(noSavePicture); 315 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 316 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 317 } 318 } 319 320 static void test_peephole() { 321 SkRandom rand; 322 323 SkPictureRecorder recorder; 324 325 for (int j = 0; j < 100; j++) { 326 SkRandom rand2(rand); // remember the seed 327 328 SkCanvas* canvas = recorder.beginRecording(100, 100); 329 330 for (int i = 0; i < 1000; ++i) { 331 rand_op(canvas, rand); 332 } 333 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 334 335 rand = rand2; 336 } 337 338 { 339 SkCanvas* canvas = recorder.beginRecording(100, 100); 340 SkRect rect = SkRect::MakeWH(50, 50); 341 342 for (int i = 0; i < 100; ++i) { 343 canvas->save(); 344 } 345 while (canvas->getSaveCount() > 1) { 346 canvas->clipRect(rect); 347 canvas->restore(); 348 } 349 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 350 } 351 } 352 353 #ifndef SK_DEBUG 354 // Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 355 // should never do this. 356 static void test_bad_bitmap() { 357 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 358 // fail. 359 SkBitmap bm; 360 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 361 SkPictureRecorder recorder; 362 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 363 recordingCanvas->drawBitmap(bm, 0, 0); 364 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 365 366 SkCanvas canvas; 367 canvas.drawPicture(picture); 368 } 369 #endif 370 371 static void test_clip_bound_opt(skiatest::Reporter* reporter) { 372 // Test for crbug.com/229011 373 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 374 SkIntToScalar(2), SkIntToScalar(2)); 375 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 376 SkIntToScalar(1), SkIntToScalar(1)); 377 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 378 SkIntToScalar(1), SkIntToScalar(1)); 379 380 SkPath invPath; 381 invPath.addOval(rect1); 382 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 383 SkPath path; 384 path.addOval(rect2); 385 SkPath path2; 386 path2.addOval(rect3); 387 SkIRect clipBounds; 388 SkPictureRecorder recorder; 389 390 // Testing conservative-raster-clip that is enabled by PictureRecord 391 { 392 SkCanvas* canvas = recorder.beginRecording(10, 10); 393 canvas->clipPath(invPath); 394 clipBounds = canvas->getDeviceClipBounds(); 395 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 396 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 397 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 398 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 399 } 400 { 401 SkCanvas* canvas = recorder.beginRecording(10, 10); 402 canvas->clipPath(path); 403 canvas->clipPath(invPath); 404 clipBounds = canvas->getDeviceClipBounds(); 405 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 406 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 407 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 408 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 409 } 410 { 411 SkCanvas* canvas = recorder.beginRecording(10, 10); 412 canvas->clipPath(path); 413 canvas->clipPath(invPath, kUnion_SkClipOp); 414 clipBounds = canvas->getDeviceClipBounds(); 415 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 416 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 417 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 418 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 419 } 420 { 421 SkCanvas* canvas = recorder.beginRecording(10, 10); 422 canvas->clipPath(path, kDifference_SkClipOp); 423 clipBounds = canvas->getDeviceClipBounds(); 424 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 425 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 426 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 427 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 428 } 429 { 430 SkCanvas* canvas = recorder.beginRecording(10, 10); 431 canvas->clipPath(path, kReverseDifference_SkClipOp); 432 clipBounds = canvas->getDeviceClipBounds(); 433 // True clip is actually empty in this case, but the best 434 // determination we can make using only bounds as input is that the 435 // clip is included in the bounds of 'path'. 436 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 437 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 438 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 439 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 440 } 441 { 442 SkCanvas* canvas = recorder.beginRecording(10, 10); 443 canvas->clipPath(path, kIntersect_SkClipOp); 444 canvas->clipPath(path2, kXOR_SkClipOp); 445 clipBounds = canvas->getDeviceClipBounds(); 446 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 447 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 448 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 449 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 450 } 451 } 452 453 static void test_cull_rect_reset(skiatest::Reporter* reporter) { 454 SkPictureRecorder recorder; 455 SkRect bounds = SkRect::MakeWH(10, 10); 456 SkRTreeFactory factory; 457 SkCanvas* canvas = recorder.beginRecording(bounds, &factory); 458 bounds = SkRect::MakeWH(100, 100); 459 SkPaint paint; 460 canvas->drawRect(bounds, paint); 461 canvas->drawRect(bounds, paint); 462 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds)); 463 const SkBigPicture* picture = SkPicturePriv::AsSkBigPicture(p); 464 REPORTER_ASSERT(reporter, picture); 465 466 SkRect finalCullRect = picture->cullRect(); 467 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft); 468 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop); 469 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom); 470 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight); 471 472 const SkBBoxHierarchy* pictureBBH = picture->bbh(); 473 SkRect bbhCullRect = pictureBBH->getRootBound(); 474 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft); 475 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop); 476 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom); 477 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight); 478 } 479 480 481 /** 482 * A canvas that records the number of clip commands. 483 */ 484 class ClipCountingCanvas : public SkCanvas { 485 public: 486 ClipCountingCanvas(int width, int height) 487 : INHERITED(width, height) 488 , fClipCount(0){ 489 } 490 491 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override { 492 fClipCount += 1; 493 this->INHERITED::onClipRect(r, op, edgeStyle); 494 } 495 496 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override { 497 fClipCount += 1; 498 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 499 } 500 501 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override { 502 fClipCount += 1; 503 this->INHERITED::onClipPath(path, op, edgeStyle); 504 } 505 506 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override { 507 fClipCount += 1; 508 this->INHERITED::onClipRegion(deviceRgn, op); 509 } 510 511 unsigned getClipCount() const { return fClipCount; } 512 513 private: 514 unsigned fClipCount; 515 516 typedef SkCanvas INHERITED; 517 }; 518 519 static void test_clip_expansion(skiatest::Reporter* reporter) { 520 SkPictureRecorder recorder; 521 SkCanvas* canvas = recorder.beginRecording(10, 10); 522 523 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp); 524 // The following expanding clip should not be skipped. 525 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp); 526 // Draw something so the optimizer doesn't just fold the world. 527 SkPaint p; 528 p.setColor(SK_ColorBLUE); 529 canvas->drawPaint(p); 530 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 531 532 ClipCountingCanvas testCanvas(10, 10); 533 picture->playback(&testCanvas); 534 535 // Both clips should be present on playback. 536 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 537 } 538 539 static void test_gen_id(skiatest::Reporter* reporter) { 540 541 SkPictureRecorder recorder; 542 recorder.beginRecording(0, 0); 543 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture()); 544 545 // Empty pictures should still have a valid ID 546 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 547 548 SkCanvas* canvas = recorder.beginRecording(1, 1); 549 canvas->drawColor(SK_ColorWHITE); 550 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture()); 551 // picture should have a non-zero id after recording 552 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 553 554 // both pictures should have different ids 555 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 556 } 557 558 static void test_typeface(skiatest::Reporter* reporter) { 559 SkPictureRecorder recorder; 560 SkCanvas* canvas = recorder.beginRecording(10, 10); 561 SkFont font(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic())); 562 canvas->drawString("Q", 0, 10, font, SkPaint()); 563 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 564 SkDynamicMemoryWStream stream; 565 picture->serialize(&stream); 566 } 567 568 DEF_TEST(Picture, reporter) { 569 test_typeface(reporter); 570 #ifdef SK_DEBUG 571 test_deleting_empty_picture(); 572 test_serializing_empty_picture(); 573 #else 574 test_bad_bitmap(); 575 #endif 576 test_unbalanced_save_restores(reporter); 577 test_peephole(); 578 test_clip_bound_opt(reporter); 579 test_clip_expansion(reporter); 580 test_gen_id(reporter); 581 test_cull_rect_reset(reporter); 582 } 583 584 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 585 const SkPaint paint; 586 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 587 const SkIRect irect = { 2, 2, 3, 3 }; 588 int divs[] = { 2, 3 }; 589 SkCanvas::Lattice lattice; 590 lattice.fXCount = lattice.fYCount = 2; 591 lattice.fXDivs = lattice.fYDivs = divs; 592 593 // Don't care what these record, as long as they're legal. 594 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 595 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); 596 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 597 canvas->drawBitmap(bitmap, 1, 1); // drawSprite 598 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); 599 } 600 601 static void test_draw_bitmaps(SkCanvas* canvas) { 602 SkBitmap empty; 603 draw_bitmaps(empty, canvas); 604 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 605 draw_bitmaps(empty, canvas); 606 } 607 608 DEF_TEST(Picture_EmptyBitmap, r) { 609 SkPictureRecorder recorder; 610 test_draw_bitmaps(recorder.beginRecording(10, 10)); 611 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 612 } 613 614 DEF_TEST(Canvas_EmptyBitmap, r) { 615 SkBitmap dst; 616 dst.allocN32Pixels(10, 10); 617 SkCanvas canvas(dst); 618 619 test_draw_bitmaps(&canvas); 620 } 621 622 DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 623 // This test is from crbug.com/344987. 624 // The commands are: 625 // saveLayer with paint that modifies alpha 626 // drawBitmapRect 627 // drawBitmapRect 628 // restore 629 // The bug was that this structure was modified so that: 630 // - The saveLayer and restore were eliminated 631 // - The alpha was only applied to the first drawBitmapRectToRect 632 633 // This test draws blue and red squares inside a 50% transparent 634 // layer. Both colours should show up muted. 635 // When the bug is present, the red square (the second bitmap) 636 // shows upwith full opacity. 637 638 SkBitmap blueBM; 639 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 640 SkBitmap redBM; 641 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 642 SkPaint semiTransparent; 643 semiTransparent.setAlpha(0x80); 644 645 SkPictureRecorder recorder; 646 SkCanvas* canvas = recorder.beginRecording(100, 100); 647 canvas->drawColor(0); 648 649 canvas->saveLayer(nullptr, &semiTransparent); 650 canvas->drawBitmap(blueBM, 25, 25); 651 canvas->drawBitmap(redBM, 50, 50); 652 canvas->restore(); 653 654 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 655 656 // Now replay the picture back on another canvas 657 // and check a couple of its pixels. 658 SkBitmap replayBM; 659 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 660 SkCanvas replayCanvas(replayBM); 661 picture->playback(&replayCanvas); 662 replayCanvas.flush(); 663 664 // With the bug present, at (55, 55) we would get a fully opaque red 665 // intead of a dark red. 666 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 667 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 668 } 669 670 struct CountingBBH : public SkBBoxHierarchy { 671 mutable int searchCalls; 672 SkRect rootBound; 673 674 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 675 676 void search(const SkRect& query, SkTDArray<int>* results) const override { 677 this->searchCalls++; 678 } 679 680 void insert(const SkRect[], int) override {} 681 virtual size_t bytesUsed() const override { return 0; } 682 SkRect getRootBound() const override { return rootBound; } 683 }; 684 685 class SpoonFedBBHFactory : public SkBBHFactory { 686 public: 687 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 688 SkBBoxHierarchy* operator()(const SkRect&) const override { 689 return SkRef(fBBH); 690 } 691 private: 692 SkBBoxHierarchy* fBBH; 693 }; 694 695 // When the canvas clip covers the full picture, we don't need to call the BBH. 696 DEF_TEST(Picture_SkipBBH, r) { 697 SkRect bound = SkRect::MakeWH(320, 240); 698 CountingBBH bbh(bound); 699 SpoonFedBBHFactory factory(&bbh); 700 701 SkPictureRecorder recorder; 702 SkCanvas* c = recorder.beginRecording(bound, &factory); 703 // Record a few ops so we don't hit a small- or empty- picture optimization. 704 c->drawRect(bound, SkPaint()); 705 c->drawRect(bound, SkPaint()); 706 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 707 708 SkCanvas big(640, 480), small(300, 200); 709 710 picture->playback(&big); 711 REPORTER_ASSERT(r, bbh.searchCalls == 0); 712 713 picture->playback(&small); 714 REPORTER_ASSERT(r, bbh.searchCalls == 1); 715 } 716 717 DEF_TEST(Picture_BitmapLeak, r) { 718 SkBitmap mut, immut; 719 mut.allocN32Pixels(300, 200); 720 immut.allocN32Pixels(300, 200); 721 immut.setImmutable(); 722 SkASSERT(!mut.isImmutable()); 723 SkASSERT(immut.isImmutable()); 724 725 // No one can hold a ref on our pixels yet. 726 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 727 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 728 729 sk_sp<SkPicture> pic; 730 { 731 // we want the recorder to go out of scope before our subsequent checks, so we 732 // place it inside local braces. 733 SkPictureRecorder rec; 734 SkCanvas* canvas = rec.beginRecording(1920, 1200); 735 canvas->drawBitmap(mut, 0, 0); 736 canvas->drawBitmap(immut, 800, 600); 737 pic = rec.finishRecordingAsPicture(); 738 } 739 740 // The picture shares the immutable pixels but copies the mutable ones. 741 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 742 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 743 744 // When the picture goes away, it's just our bitmaps holding the refs. 745 pic = nullptr; 746 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 747 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 748 } 749 750 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 751 DEF_TEST(Picture_getRecordingCanvas, r) { 752 SkPictureRecorder rec; 753 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 754 for (int i = 0; i < 3; i++) { 755 rec.beginRecording(100, 100); 756 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 757 rec.finishRecordingAsPicture(); 758 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 759 } 760 } 761 762 DEF_TEST(MiniRecorderLeftHanging, r) { 763 // Any shader or other ref-counted effect will do just fine here. 764 SkPaint paint; 765 paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); 766 767 SkMiniRecorder rec; 768 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 769 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 770 } 771 772 DEF_TEST(Picture_preserveCullRect, r) { 773 SkPictureRecorder recorder; 774 775 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 776 c->clear(SK_ColorCYAN); 777 778 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 779 SkDynamicMemoryWStream wstream; 780 picture->serialize(&wstream); 781 782 std::unique_ptr<SkStream> rstream(wstream.detachAsStream()); 783 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get())); 784 785 REPORTER_ASSERT(r, deserializedPicture != nullptr); 786 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 787 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 788 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 789 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 790 } 791 792 793 // If we record bounded ops into a picture with a big cull and calculate the 794 // bounds of those ops, we should trim down the picture cull to the ops' bounds. 795 // If we're not using an SkBBH, we shouldn't change it. 796 DEF_TEST(Picture_UpdatedCull_1, r) { 797 // Testing 1 draw exercises SkMiniPicture. 798 SkRTreeFactory factory; 799 SkPictureRecorder recorder; 800 801 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory); 802 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 803 auto pic = recorder.finishRecordingAsPicture(); 804 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20)); 805 806 canvas = recorder.beginRecording(SkRectPriv::MakeLargest()); 807 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 808 pic = recorder.finishRecordingAsPicture(); 809 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest()); 810 } 811 DEF_TEST(Picture_UpdatedCull_2, r) { 812 // Testing >1 draw exercises SkBigPicture. 813 SkRTreeFactory factory; 814 SkPictureRecorder recorder; 815 816 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory); 817 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 818 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); 819 auto pic = recorder.finishRecordingAsPicture(); 820 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40)); 821 822 canvas = recorder.beginRecording(SkRectPriv::MakeLargest()); 823 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 824 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); 825 pic = recorder.finishRecordingAsPicture(); 826 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest()); 827 } 828 829 DEF_TEST(Picture_RecordsFlush, r) { 830 SkPictureRecorder recorder; 831 832 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100)); 833 for (int i = 0; i < 10; i++) { 834 canvas->clear(0); 835 for (int j = 0; j < 10; j++) { 836 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint()); 837 } 838 canvas->flush(); 839 } 840 841 // Did we record the flushes? 842 auto pic = recorder.finishRecordingAsPicture(); 843 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes 844 845 // Do we serialize and deserialize flushes? 846 auto skp = pic->serialize(); 847 auto back = SkPicture::MakeFromData(skp->data(), skp->size()); 848 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount()); 849 } 850 851 DEF_TEST(Placeholder, r) { 852 SkRect cull = { 0,0, 10,20 }; 853 854 // Each placeholder is unique. 855 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull), 856 p2 = SkPicture::MakePlaceholder(cull); 857 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect()); 858 REPORTER_ASSERT(r, p1->cullRect() == cull); 859 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID()); 860 861 // Placeholders are never unrolled by SkCanvas (while other small pictures may be). 862 SkPictureRecorder recorder; 863 SkCanvas* canvas = recorder.beginRecording(cull); 864 canvas->drawPicture(p1); 865 canvas->drawPicture(p2); 866 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture(); 867 REPORTER_ASSERT(r, pic->approximateOpCount() == 2); 868 } 869 870 DEF_TEST(Picture_empty_serial, reporter) { 871 SkPictureRecorder rec; 872 (void)rec.beginRecording(10, 10); 873 auto pic = rec.finishRecordingAsPicture(); 874 REPORTER_ASSERT(reporter, pic); 875 876 auto data = pic->serialize(); 877 REPORTER_ASSERT(reporter, data); 878 879 auto pic2 = SkPicture::MakeFromData(data->data(), data->size()); 880 REPORTER_ASSERT(reporter, pic2); 881 } 882 883