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 663 // With the bug present, at (55, 55) we would get a fully opaque red 664 // intead of a dark red. 665 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 666 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 667 } 668 669 struct CountingBBH : public SkBBoxHierarchy { 670 mutable int searchCalls; 671 SkRect rootBound; 672 673 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 674 675 void search(const SkRect& query, SkTDArray<int>* results) const override { 676 this->searchCalls++; 677 } 678 679 void insert(const SkRect[], int) override {} 680 virtual size_t bytesUsed() const override { return 0; } 681 SkRect getRootBound() const override { return rootBound; } 682 }; 683 684 class SpoonFedBBHFactory : public SkBBHFactory { 685 public: 686 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 687 SkBBoxHierarchy* operator()(const SkRect&) const override { 688 return SkRef(fBBH); 689 } 690 private: 691 SkBBoxHierarchy* fBBH; 692 }; 693 694 // When the canvas clip covers the full picture, we don't need to call the BBH. 695 DEF_TEST(Picture_SkipBBH, r) { 696 SkRect bound = SkRect::MakeWH(320, 240); 697 CountingBBH bbh(bound); 698 SpoonFedBBHFactory factory(&bbh); 699 700 SkPictureRecorder recorder; 701 SkCanvas* c = recorder.beginRecording(bound, &factory); 702 // Record a few ops so we don't hit a small- or empty- picture optimization. 703 c->drawRect(bound, SkPaint()); 704 c->drawRect(bound, SkPaint()); 705 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 706 707 SkCanvas big(640, 480), small(300, 200); 708 709 picture->playback(&big); 710 REPORTER_ASSERT(r, bbh.searchCalls == 0); 711 712 picture->playback(&small); 713 REPORTER_ASSERT(r, bbh.searchCalls == 1); 714 } 715 716 DEF_TEST(Picture_BitmapLeak, r) { 717 SkBitmap mut, immut; 718 mut.allocN32Pixels(300, 200); 719 immut.allocN32Pixels(300, 200); 720 immut.setImmutable(); 721 SkASSERT(!mut.isImmutable()); 722 SkASSERT(immut.isImmutable()); 723 724 // No one can hold a ref on our pixels yet. 725 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 726 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 727 728 sk_sp<SkPicture> pic; 729 { 730 // we want the recorder to go out of scope before our subsequent checks, so we 731 // place it inside local braces. 732 SkPictureRecorder rec; 733 SkCanvas* canvas = rec.beginRecording(1920, 1200); 734 canvas->drawBitmap(mut, 0, 0); 735 canvas->drawBitmap(immut, 800, 600); 736 pic = rec.finishRecordingAsPicture(); 737 } 738 739 // The picture shares the immutable pixels but copies the mutable ones. 740 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 741 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 742 743 // When the picture goes away, it's just our bitmaps holding the refs. 744 pic = nullptr; 745 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 746 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 747 } 748 749 // getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 750 DEF_TEST(Picture_getRecordingCanvas, r) { 751 SkPictureRecorder rec; 752 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 753 for (int i = 0; i < 3; i++) { 754 rec.beginRecording(100, 100); 755 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 756 rec.finishRecordingAsPicture(); 757 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 758 } 759 } 760 761 DEF_TEST(MiniRecorderLeftHanging, r) { 762 // Any shader or other ref-counted effect will do just fine here. 763 SkPaint paint; 764 paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); 765 766 SkMiniRecorder rec; 767 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 768 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 769 } 770 771 DEF_TEST(Picture_preserveCullRect, r) { 772 SkPictureRecorder recorder; 773 774 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 775 c->clear(SK_ColorCYAN); 776 777 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 778 SkDynamicMemoryWStream wstream; 779 picture->serialize(&wstream); 780 781 std::unique_ptr<SkStream> rstream(wstream.detachAsStream()); 782 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get())); 783 784 REPORTER_ASSERT(r, deserializedPicture != nullptr); 785 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 786 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 787 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 788 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 789 } 790 791 792 // If we record bounded ops into a picture with a big cull and calculate the 793 // bounds of those ops, we should trim down the picture cull to the ops' bounds. 794 // If we're not using an SkBBH, we shouldn't change it. 795 DEF_TEST(Picture_UpdatedCull_1, r) { 796 // Testing 1 draw exercises SkMiniPicture. 797 SkRTreeFactory factory; 798 SkPictureRecorder recorder; 799 800 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory); 801 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 802 auto pic = recorder.finishRecordingAsPicture(); 803 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20)); 804 805 canvas = recorder.beginRecording(SkRectPriv::MakeLargest()); 806 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 807 pic = recorder.finishRecordingAsPicture(); 808 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest()); 809 } 810 DEF_TEST(Picture_UpdatedCull_2, r) { 811 // Testing >1 draw exercises SkBigPicture. 812 SkRTreeFactory factory; 813 SkPictureRecorder recorder; 814 815 auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory); 816 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 817 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); 818 auto pic = recorder.finishRecordingAsPicture(); 819 REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40)); 820 821 canvas = recorder.beginRecording(SkRectPriv::MakeLargest()); 822 canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); 823 canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); 824 pic = recorder.finishRecordingAsPicture(); 825 REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest()); 826 } 827 828 DEF_TEST(Picture_RecordsFlush, r) { 829 SkPictureRecorder recorder; 830 831 auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100)); 832 for (int i = 0; i < 10; i++) { 833 canvas->clear(0); 834 for (int j = 0; j < 10; j++) { 835 canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint()); 836 } 837 canvas->flush(); 838 } 839 840 // Did we record the flushes? 841 auto pic = recorder.finishRecordingAsPicture(); 842 REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes 843 844 // Do we serialize and deserialize flushes? 845 auto skp = pic->serialize(); 846 auto back = SkPicture::MakeFromData(skp->data(), skp->size()); 847 REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount()); 848 } 849 850 DEF_TEST(Placeholder, r) { 851 SkRect cull = { 0,0, 10,20 }; 852 853 // Each placeholder is unique. 854 sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull), 855 p2 = SkPicture::MakePlaceholder(cull); 856 REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect()); 857 REPORTER_ASSERT(r, p1->cullRect() == cull); 858 REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID()); 859 860 // Placeholders are never unrolled by SkCanvas (while other small pictures may be). 861 SkPictureRecorder recorder; 862 SkCanvas* canvas = recorder.beginRecording(cull); 863 canvas->drawPicture(p1); 864 canvas->drawPicture(p2); 865 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture(); 866 REPORTER_ASSERT(r, pic->approximateOpCount() == 2); 867 } 868 869 DEF_TEST(Picture_empty_serial, reporter) { 870 SkPictureRecorder rec; 871 (void)rec.beginRecording(10, 10); 872 auto pic = rec.finishRecordingAsPicture(); 873 REPORTER_ASSERT(reporter, pic); 874 875 auto data = pic->serialize(); 876 REPORTER_ASSERT(reporter, data); 877 878 auto pic2 = SkPicture::MakeFromData(data->data(), data->size()); 879 REPORTER_ASSERT(reporter, pic2); 880 } 881 882