PictureTest.cpp revision 7614794c9ad14d76abed6cf00890ad1a09c2cb8b
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 "SkBigPicture.h" 9#include "SkBBoxHierarchy.h" 10#include "SkBlurImageFilter.h" 11#include "SkCanvas.h" 12#include "SkColorMatrixFilter.h" 13#include "SkColorPriv.h" 14#include "SkDashPathEffect.h" 15#include "SkData.h" 16#include "SkImageGenerator.h" 17#include "SkImageEncoder.h" 18#include "SkImageGenerator.h" 19#include "SkMD5.h" 20#include "SkPaint.h" 21#include "SkPicture.h" 22#include "SkPictureAnalyzer.h" 23#include "SkPictureRecorder.h" 24#include "SkPictureUtils.h" 25#include "SkPixelRef.h" 26#include "SkPixelSerializer.h" 27#include "SkMiniRecorder.h" 28#include "SkRRect.h" 29#include "SkRandom.h" 30#include "SkRecord.h" 31#include "SkShader.h" 32#include "SkStream.h" 33#include "sk_tool_utils.h" 34 35#include "Test.h" 36 37#include "SkLumaColorFilter.h" 38#include "SkColorFilterImageFilter.h" 39 40static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 41 bm->allocN32Pixels(w, h); 42 bm->eraseColor(color); 43 if (immutable) { 44 bm->setImmutable(); 45 } 46} 47 48// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps. 49static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) { 50 // We just need _some_ SkImage 51 const SkPMColor pixel = 0; 52 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); 53 sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel)))); 54 55 SkPictureRecorder recorder; 56 recorder.beginRecording(100,100)->drawImage(image, 0,0); 57 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 58 59 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps()); 60} 61 62/* Hit a few SkPicture::Analysis cases not handled elsewhere. */ 63static void test_analysis(skiatest::Reporter* reporter) { 64 SkPictureRecorder recorder; 65 66 SkCanvas* canvas = recorder.beginRecording(100, 100); 67 { 68 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ()); 69 } 70 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 71 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps()); 72 73 canvas = recorder.beginRecording(100, 100); 74 { 75 SkPaint paint; 76 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader 77 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here. 78 SkBitmap bitmap; 79 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2)); 80 bitmap.eraseColor(SK_ColorBLUE); 81 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN; 82 paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode, 83 SkShader::kClamp_TileMode)); 84 REPORTER_ASSERT(reporter, paint.getShader()->isAImage()); 85 86 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 87 } 88 REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps()); 89} 90 91 92#ifdef SK_DEBUG 93// Ensure that deleting an empty SkPicture does not assert. Asserts only fire 94// in debug mode, so only run in debug mode. 95static void test_deleting_empty_picture() { 96 SkPictureRecorder recorder; 97 // Creates an SkPictureRecord 98 recorder.beginRecording(0, 0); 99 // Turns that into an SkPicture 100 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 101 // Ceates a new SkPictureRecord 102 recorder.beginRecording(0, 0); 103} 104 105// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 106static void test_serializing_empty_picture() { 107 SkPictureRecorder recorder; 108 recorder.beginRecording(0, 0); 109 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 110 SkDynamicMemoryWStream stream; 111 picture->serialize(&stream); 112} 113#endif 114 115static void rand_op(SkCanvas* canvas, SkRandom& rand) { 116 SkPaint paint; 117 SkRect rect = SkRect::MakeWH(50, 50); 118 119 SkScalar unit = rand.nextUScalar1(); 120 if (unit <= 0.3) { 121// SkDebugf("save\n"); 122 canvas->save(); 123 } else if (unit <= 0.6) { 124// SkDebugf("restore\n"); 125 canvas->restore(); 126 } else if (unit <= 0.9) { 127// SkDebugf("clip\n"); 128 canvas->clipRect(rect); 129 } else { 130// SkDebugf("draw\n"); 131 canvas->drawPaint(paint); 132 } 133} 134 135#if SK_SUPPORT_GPU 136 137static SkPath make_convex_path() { 138 SkPath path; 139 path.lineTo(100, 0); 140 path.lineTo(50, 100); 141 path.close(); 142 143 return path; 144} 145 146static SkPath make_concave_path() { 147 SkPath path; 148 path.lineTo(50, 50); 149 path.lineTo(100, 0); 150 path.lineTo(50, 100); 151 path.close(); 152 153 return path; 154} 155 156static void test_gpu_veto(skiatest::Reporter* reporter) { 157 SkPictureRecorder recorder; 158 159 SkCanvas* canvas = recorder.beginRecording(100, 100); 160 { 161 SkPath path; 162 path.moveTo(0, 0); 163 path.lineTo(50, 50); 164 165 SkScalar intervals[] = { 1.0f, 1.0f }; 166 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); 167 168 SkPaint paint; 169 paint.setStyle(SkPaint::kStroke_Style); 170 paint.setPathEffect(dash); 171 172 for (int i = 0; i < 50; ++i) { 173 canvas->drawPath(path, paint); 174 } 175 } 176 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 177 // path effects currently render an SkPicture undesireable for GPU rendering 178 179 const char *reason = nullptr; 180 REPORTER_ASSERT(reporter, 181 !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason)); 182 REPORTER_ASSERT(reporter, reason); 183 184 canvas = recorder.beginRecording(100, 100); 185 { 186 SkPath path; 187 188 path.moveTo(0, 0); 189 path.lineTo(0, 50); 190 path.lineTo(25, 25); 191 path.lineTo(50, 50); 192 path.lineTo(50, 0); 193 path.close(); 194 REPORTER_ASSERT(reporter, !path.isConvex()); 195 196 SkPaint paint; 197 paint.setAntiAlias(true); 198 for (int i = 0; i < 50; ++i) { 199 canvas->drawPath(path, paint); 200 } 201 } 202 picture = recorder.finishRecordingAsPicture(); 203 // A lot of small AA concave paths should be fine for GPU rendering 204 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 205 206 canvas = recorder.beginRecording(100, 100); 207 { 208 SkPath path; 209 210 path.moveTo(0, 0); 211 path.lineTo(0, 100); 212 path.lineTo(50, 50); 213 path.lineTo(100, 100); 214 path.lineTo(100, 0); 215 path.close(); 216 REPORTER_ASSERT(reporter, !path.isConvex()); 217 218 SkPaint paint; 219 paint.setAntiAlias(true); 220 for (int i = 0; i < 50; ++i) { 221 canvas->drawPath(path, paint); 222 } 223 } 224 picture = recorder.finishRecordingAsPicture(); 225 // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering 226 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 227 228 canvas = recorder.beginRecording(100, 100); 229 { 230 SkPath path; 231 232 path.moveTo(0, 0); 233 path.lineTo(0, 50); 234 path.lineTo(25, 25); 235 path.lineTo(50, 50); 236 path.lineTo(50, 0); 237 path.close(); 238 REPORTER_ASSERT(reporter, !path.isConvex()); 239 240 SkPaint paint; 241 paint.setAntiAlias(true); 242 paint.setStyle(SkPaint::kStroke_Style); 243 paint.setStrokeWidth(0); 244 for (int i = 0; i < 50; ++i) { 245 canvas->drawPath(path, paint); 246 } 247 } 248 picture = recorder.finishRecordingAsPicture(); 249 // hairline stroked AA concave paths are fine for GPU rendering 250 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 251 252 canvas = recorder.beginRecording(100, 100); 253 { 254 SkPaint paint; 255 SkScalar intervals [] = { 10, 20 }; 256 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); 257 258 SkPoint points [2] = { { 0, 0 }, { 100, 0 } }; 259 260 for (int i = 0; i < 50; ++i) { 261 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); 262 } 263 } 264 picture = recorder.finishRecordingAsPicture(); 265 // fast-path dashed effects are fine for GPU rendering ... 266 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 267 268 canvas = recorder.beginRecording(100, 100); 269 { 270 SkPaint paint; 271 SkScalar intervals [] = { 10, 20 }; 272 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); 273 274 for (int i = 0; i < 50; ++i) { 275 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 276 } 277 } 278 picture = recorder.finishRecordingAsPicture(); 279 // ... but only when applied to drawPoint() calls 280 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 281 282 canvas = recorder.beginRecording(100, 100); 283 { 284 const SkPath convexClip = make_convex_path(); 285 const SkPath concaveClip = make_concave_path(); 286 287 for (int i = 0; i < 50; ++i) { 288 canvas->clipPath(convexClip); 289 canvas->clipPath(concaveClip); 290 canvas->clipPath(convexClip, SkCanvas::kIntersect_Op, true); 291 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); 292 } 293 } 294 picture = recorder.finishRecordingAsPicture(); 295 // Convex clips and non-AA concave clips are fine on the GPU. 296 REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 297 298 canvas = recorder.beginRecording(100, 100); 299 { 300 const SkPath concaveClip = make_concave_path(); 301 for (int i = 0; i < 50; ++i) { 302 canvas->clipPath(concaveClip, SkCanvas::kIntersect_Op, true); 303 canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); 304 } 305 } 306 picture = recorder.finishRecordingAsPicture(); 307 // ... but AA concave clips are not. 308 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 309 310 // Nest the previous picture inside a new one. 311 canvas = recorder.beginRecording(100, 100); 312 { 313 canvas->drawPicture(picture); 314 } 315 picture = recorder.finishRecordingAsPicture(); 316 REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); 317} 318 319#endif // SK_SUPPORT_GPU 320 321static void set_canvas_to_save_count_4(SkCanvas* canvas) { 322 canvas->restoreToCount(1); 323 canvas->save(); 324 canvas->save(); 325 canvas->save(); 326} 327 328/** 329 * A canvas that records the number of saves, saveLayers and restores. 330 */ 331class SaveCountingCanvas : public SkCanvas { 332public: 333 SaveCountingCanvas(int width, int height) 334 : INHERITED(width, height) 335 , fSaveCount(0) 336 , fSaveLayerCount(0) 337 , fRestoreCount(0){ 338 } 339 340 SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { 341 ++fSaveLayerCount; 342 return this->INHERITED::getSaveLayerStrategy(rec); 343 } 344 345 void willSave() override { 346 ++fSaveCount; 347 this->INHERITED::willSave(); 348 } 349 350 void willRestore() override { 351 ++fRestoreCount; 352 this->INHERITED::willRestore(); 353 } 354 355 unsigned int getSaveCount() const { return fSaveCount; } 356 unsigned int getSaveLayerCount() const { return fSaveLayerCount; } 357 unsigned int getRestoreCount() const { return fRestoreCount; } 358 359private: 360 unsigned int fSaveCount; 361 unsigned int fSaveLayerCount; 362 unsigned int fRestoreCount; 363 364 typedef SkCanvas INHERITED; 365}; 366 367void check_save_state(skiatest::Reporter* reporter, SkPicture* picture, 368 unsigned int numSaves, unsigned int numSaveLayers, 369 unsigned int numRestores) { 370 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()), 371 SkScalarCeilToInt(picture->cullRect().height())); 372 373 picture->playback(&canvas); 374 375 // Optimizations may have removed these, 376 // so expect to have seen no more than num{Saves,SaveLayers,Restores}. 377 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount()); 378 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount()); 379 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount()); 380} 381 382// This class exists so SkPicture can friend it and give it access to 383// the 'partialReplay' method. 384class SkPictureRecorderReplayTester { 385public: 386 static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) { 387 SkPictureRecorder recorder2; 388 389 SkCanvas* canvas = recorder2.beginRecording(10, 10); 390 391 recorder->partialReplay(canvas); 392 393 return recorder2.finishRecordingAsPicture(); 394 } 395}; 396 397static void create_imbalance(SkCanvas* canvas) { 398 SkRect clipRect = SkRect::MakeWH(2, 2); 399 SkRect drawRect = SkRect::MakeWH(10, 10); 400 canvas->save(); 401 canvas->clipRect(clipRect, SkCanvas::kReplace_Op); 402 canvas->translate(1.0f, 1.0f); 403 SkPaint p; 404 p.setColor(SK_ColorGREEN); 405 canvas->drawRect(drawRect, p); 406 // no restore 407} 408 409// This tests that replaying a potentially unbalanced picture into a canvas 410// doesn't affect the canvas' save count or matrix/clip state. 411static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) { 412 SkBitmap bm; 413 bm.allocN32Pixels(4, 3); 414 SkCanvas canvas(bm); 415 416 int beforeSaveCount = canvas.getSaveCount(); 417 418 SkMatrix beforeMatrix = canvas.getTotalMatrix(); 419 420 SkRect beforeClip; 421 422 canvas.getClipBounds(&beforeClip); 423 424 canvas.drawPicture(picture); 425 426 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 427 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 428 429 SkRect afterClip; 430 431 canvas.getClipBounds(&afterClip); 432 433 REPORTER_ASSERT(reporter, afterClip == beforeClip); 434} 435 436// Test out SkPictureRecorder::partialReplay 437DEF_TEST(PictureRecorder_replay, reporter) { 438 // check save/saveLayer state 439 { 440 SkPictureRecorder recorder; 441 442 SkCanvas* canvas = recorder.beginRecording(10, 10); 443 444 canvas->saveLayer(nullptr, nullptr); 445 446 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 447 448 // The extra save and restore comes from the Copy process. 449 check_save_state(reporter, copy.get(), 2, 1, 3); 450 451 canvas->saveLayer(nullptr, nullptr); 452 453 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 454 455 check_save_state(reporter, final.get(), 1, 2, 3); 456 457 // The copy shouldn't pick up any operations added after it was made 458 check_save_state(reporter, copy.get(), 2, 1, 3); 459 } 460 461 // (partially) check leakage of draw ops 462 { 463 SkPictureRecorder recorder; 464 465 SkCanvas* canvas = recorder.beginRecording(10, 10); 466 467 SkRect r = SkRect::MakeWH(5, 5); 468 SkPaint p; 469 470 canvas->drawRect(r, p); 471 472 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 473 474 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 475 476 SkBitmap bm; 477 make_bm(&bm, 10, 10, SK_ColorRED, true); 478 479 r.offset(5.0f, 5.0f); 480 canvas->drawBitmapRect(bm, r, nullptr); 481 482 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 483 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); 484 485 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); 486 487 // The snapshot shouldn't pick up any operations added after it was made 488 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 489 } 490 491 // Recreate the Android partialReplay test case 492 { 493 SkPictureRecorder recorder; 494 495 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0); 496 create_imbalance(canvas); 497 498 int expectedSaveCount = canvas->getSaveCount(); 499 500 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 501 check_balance(reporter, copy.get()); 502 503 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 504 505 // End the recording of source to test the picture finalization 506 // process isn't complicated by the partialReplay step 507 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 508 } 509} 510 511static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 512 SkCanvas testCanvas(100, 100); 513 set_canvas_to_save_count_4(&testCanvas); 514 515 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 516 517 SkPaint paint; 518 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 519 520 SkPictureRecorder recorder; 521 522 { 523 // Create picture with 2 unbalanced saves 524 SkCanvas* canvas = recorder.beginRecording(100, 100); 525 canvas->save(); 526 canvas->translate(10, 10); 527 canvas->drawRect(rect, paint); 528 canvas->save(); 529 canvas->translate(10, 10); 530 canvas->drawRect(rect, paint); 531 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture()); 532 533 testCanvas.drawPicture(extraSavePicture); 534 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 535 } 536 537 set_canvas_to_save_count_4(&testCanvas); 538 539 { 540 // Create picture with 2 unbalanced restores 541 SkCanvas* canvas = recorder.beginRecording(100, 100); 542 canvas->save(); 543 canvas->translate(10, 10); 544 canvas->drawRect(rect, paint); 545 canvas->save(); 546 canvas->translate(10, 10); 547 canvas->drawRect(rect, paint); 548 canvas->restore(); 549 canvas->restore(); 550 canvas->restore(); 551 canvas->restore(); 552 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture()); 553 554 testCanvas.drawPicture(extraRestorePicture); 555 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 556 } 557 558 set_canvas_to_save_count_4(&testCanvas); 559 560 { 561 SkCanvas* canvas = recorder.beginRecording(100, 100); 562 canvas->translate(10, 10); 563 canvas->drawRect(rect, paint); 564 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture()); 565 566 testCanvas.drawPicture(noSavePicture); 567 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 568 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 569 } 570} 571 572static void test_peephole() { 573 SkRandom rand; 574 575 SkPictureRecorder recorder; 576 577 for (int j = 0; j < 100; j++) { 578 SkRandom rand2(rand); // remember the seed 579 580 SkCanvas* canvas = recorder.beginRecording(100, 100); 581 582 for (int i = 0; i < 1000; ++i) { 583 rand_op(canvas, rand); 584 } 585 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 586 587 rand = rand2; 588 } 589 590 { 591 SkCanvas* canvas = recorder.beginRecording(100, 100); 592 SkRect rect = SkRect::MakeWH(50, 50); 593 594 for (int i = 0; i < 100; ++i) { 595 canvas->save(); 596 } 597 while (canvas->getSaveCount() > 1) { 598 canvas->clipRect(rect); 599 canvas->restore(); 600 } 601 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 602 } 603} 604 605#ifndef SK_DEBUG 606// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 607// should never do this. 608static void test_bad_bitmap() { 609 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 610 // fail. 611 SkBitmap bm; 612 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 613 SkPictureRecorder recorder; 614 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 615 recordingCanvas->drawBitmap(bm, 0, 0); 616 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 617 618 SkCanvas canvas; 619 canvas.drawPicture(picture); 620} 621#endif 622 623static void test_clip_bound_opt(skiatest::Reporter* reporter) { 624 // Test for crbug.com/229011 625 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 626 SkIntToScalar(2), SkIntToScalar(2)); 627 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 628 SkIntToScalar(1), SkIntToScalar(1)); 629 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 630 SkIntToScalar(1), SkIntToScalar(1)); 631 632 SkPath invPath; 633 invPath.addOval(rect1); 634 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 635 SkPath path; 636 path.addOval(rect2); 637 SkPath path2; 638 path2.addOval(rect3); 639 SkIRect clipBounds; 640 SkPictureRecorder recorder; 641 642 // Testing conservative-raster-clip that is enabled by PictureRecord 643 { 644 SkCanvas* canvas = recorder.beginRecording(10, 10); 645 canvas->clipPath(invPath); 646 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 647 REPORTER_ASSERT(reporter, true == nonEmpty); 648 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 649 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 650 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 651 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 652 } 653 { 654 SkCanvas* canvas = recorder.beginRecording(10, 10); 655 canvas->clipPath(path); 656 canvas->clipPath(invPath); 657 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 658 REPORTER_ASSERT(reporter, true == nonEmpty); 659 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 660 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 661 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 662 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 663 } 664 { 665 SkCanvas* canvas = recorder.beginRecording(10, 10); 666 canvas->clipPath(path); 667 canvas->clipPath(invPath, SkCanvas::kUnion_Op); 668 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 669 REPORTER_ASSERT(reporter, true == nonEmpty); 670 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 671 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 672 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 673 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 674 } 675 { 676 SkCanvas* canvas = recorder.beginRecording(10, 10); 677 canvas->clipPath(path, SkCanvas::kDifference_Op); 678 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 679 REPORTER_ASSERT(reporter, true == nonEmpty); 680 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 681 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 682 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 683 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 684 } 685 { 686 SkCanvas* canvas = recorder.beginRecording(10, 10); 687 canvas->clipPath(path, SkCanvas::kReverseDifference_Op); 688 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 689 // True clip is actually empty in this case, but the best 690 // determination we can make using only bounds as input is that the 691 // clip is included in the bounds of 'path'. 692 REPORTER_ASSERT(reporter, true == nonEmpty); 693 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 694 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 695 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 696 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 697 } 698 { 699 SkCanvas* canvas = recorder.beginRecording(10, 10); 700 canvas->clipPath(path, SkCanvas::kIntersect_Op); 701 canvas->clipPath(path2, SkCanvas::kXOR_Op); 702 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 703 REPORTER_ASSERT(reporter, true == nonEmpty); 704 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 705 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 706 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 707 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 708 } 709} 710 711static void test_cull_rect_reset(skiatest::Reporter* reporter) { 712 SkPictureRecorder recorder; 713 SkRect bounds = SkRect::MakeWH(10, 10); 714 SkRTreeFactory factory; 715 SkCanvas* canvas = recorder.beginRecording(bounds, &factory); 716 bounds = SkRect::MakeWH(100, 100); 717 SkPaint paint; 718 canvas->drawRect(bounds, paint); 719 canvas->drawRect(bounds, paint); 720 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds)); 721 const SkBigPicture* picture = p->asSkBigPicture(); 722 REPORTER_ASSERT(reporter, picture); 723 724 SkRect finalCullRect = picture->cullRect(); 725 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft); 726 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop); 727 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom); 728 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight); 729 730 const SkBBoxHierarchy* pictureBBH = picture->bbh(); 731 SkRect bbhCullRect = pictureBBH->getRootBound(); 732 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft); 733 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop); 734 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom); 735 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight); 736} 737 738 739/** 740 * A canvas that records the number of clip commands. 741 */ 742class ClipCountingCanvas : public SkCanvas { 743public: 744 ClipCountingCanvas(int width, int height) 745 : INHERITED(width, height) 746 , fClipCount(0){ 747 } 748 749 void onClipRect(const SkRect& r, ClipOp op, ClipEdgeStyle edgeStyle) override { 750 fClipCount += 1; 751 this->INHERITED::onClipRect(r, op, edgeStyle); 752 } 753 754 void onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle)override { 755 fClipCount += 1; 756 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 757 } 758 759 void onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) override { 760 fClipCount += 1; 761 this->INHERITED::onClipPath(path, op, edgeStyle); 762 } 763 764 void onClipRegion(const SkRegion& deviceRgn, ClipOp op) override { 765 fClipCount += 1; 766 this->INHERITED::onClipRegion(deviceRgn, op); 767 } 768 769 unsigned getClipCount() const { return fClipCount; } 770 771private: 772 unsigned fClipCount; 773 774 typedef SkCanvas INHERITED; 775}; 776 777static void test_clip_expansion(skiatest::Reporter* reporter) { 778 SkPictureRecorder recorder; 779 SkCanvas* canvas = recorder.beginRecording(10, 10); 780 781 canvas->clipRect(SkRect::MakeEmpty(), SkCanvas::kReplace_Op); 782 // The following expanding clip should not be skipped. 783 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkCanvas::kUnion_Op); 784 // Draw something so the optimizer doesn't just fold the world. 785 SkPaint p; 786 p.setColor(SK_ColorBLUE); 787 canvas->drawPaint(p); 788 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 789 790 ClipCountingCanvas testCanvas(10, 10); 791 picture->playback(&testCanvas); 792 793 // Both clips should be present on playback. 794 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 795} 796 797static void test_hierarchical(skiatest::Reporter* reporter) { 798 SkBitmap bm; 799 make_bm(&bm, 10, 10, SK_ColorRED, true); 800 801 SkPictureRecorder recorder; 802 803 recorder.beginRecording(10, 10); 804 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture()); 805 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 806 807 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 808 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture()); 809 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 810 811 { 812 SkCanvas* canvas = recorder.beginRecording(10, 10); 813 canvas->drawPicture(childPlain); 814 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture()); 815 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 816 } 817 { 818 SkCanvas* canvas = recorder.beginRecording(10, 10); 819 canvas->drawPicture(childWithBitmap); 820 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture()); 821 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 822 } 823 { 824 SkCanvas* canvas = recorder.beginRecording(10, 10); 825 canvas->drawBitmap(bm, 0, 0); 826 canvas->drawPicture(childPlain); 827 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture()); 828 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 829 } 830 { 831 SkCanvas* canvas = recorder.beginRecording(10, 10); 832 canvas->drawBitmap(bm, 0, 0); 833 canvas->drawPicture(childWithBitmap); 834 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture()); 835 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 836 } 837} 838 839static void test_gen_id(skiatest::Reporter* reporter) { 840 841 SkPictureRecorder recorder; 842 recorder.beginRecording(0, 0); 843 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture()); 844 845 // Empty pictures should still have a valid ID 846 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 847 848 SkCanvas* canvas = recorder.beginRecording(1, 1); 849 canvas->drawARGB(255, 255, 255, 255); 850 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture()); 851 // picture should have a non-zero id after recording 852 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 853 854 // both pictures should have different ids 855 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 856} 857 858static void test_typeface(skiatest::Reporter* reporter) { 859 SkPictureRecorder recorder; 860 SkCanvas* canvas = recorder.beginRecording(10, 10); 861 SkPaint paint; 862 paint.setTypeface(SkTypeface::MakeFromName("Arial", 863 SkFontStyle::FromOldStyle(SkTypeface::kItalic))); 864 canvas->drawText("Q", 1, 0, 10, paint); 865 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 866 SkDynamicMemoryWStream stream; 867 picture->serialize(&stream); 868} 869 870DEF_TEST(Picture, reporter) { 871 test_typeface(reporter); 872#ifdef SK_DEBUG 873 test_deleting_empty_picture(); 874 test_serializing_empty_picture(); 875#else 876 test_bad_bitmap(); 877#endif 878 test_unbalanced_save_restores(reporter); 879 test_peephole(); 880#if SK_SUPPORT_GPU 881 test_gpu_veto(reporter); 882#endif 883 test_images_are_found_by_willPlayBackBitmaps(reporter); 884 test_analysis(reporter); 885 test_clip_bound_opt(reporter); 886 test_clip_expansion(reporter); 887 test_hierarchical(reporter); 888 test_gen_id(reporter); 889 test_cull_rect_reset(reporter); 890} 891 892static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 893 const SkPaint paint; 894 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 895 const SkIRect irect = { 2, 2, 3, 3 }; 896 int divs[] = { 2, 3 }; 897 SkCanvas::Lattice lattice; 898 lattice.fXCount = lattice.fYCount = 2; 899 lattice.fXDivs = lattice.fYDivs = divs; 900 901 // Don't care what these record, as long as they're legal. 902 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 903 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); 904 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 905 canvas->drawBitmap(bitmap, 1, 1); // drawSprite 906 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); 907} 908 909static void test_draw_bitmaps(SkCanvas* canvas) { 910 SkBitmap empty; 911 draw_bitmaps(empty, canvas); 912 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 913 draw_bitmaps(empty, canvas); 914} 915 916DEF_TEST(Picture_EmptyBitmap, r) { 917 SkPictureRecorder recorder; 918 test_draw_bitmaps(recorder.beginRecording(10, 10)); 919 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 920} 921 922DEF_TEST(Canvas_EmptyBitmap, r) { 923 SkBitmap dst; 924 dst.allocN32Pixels(10, 10); 925 SkCanvas canvas(dst); 926 927 test_draw_bitmaps(&canvas); 928} 929 930DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 931 // This test is from crbug.com/344987. 932 // The commands are: 933 // saveLayer with paint that modifies alpha 934 // drawBitmapRect 935 // drawBitmapRect 936 // restore 937 // The bug was that this structure was modified so that: 938 // - The saveLayer and restore were eliminated 939 // - The alpha was only applied to the first drawBitmapRectToRect 940 941 // This test draws blue and red squares inside a 50% transparent 942 // layer. Both colours should show up muted. 943 // When the bug is present, the red square (the second bitmap) 944 // shows upwith full opacity. 945 946 SkBitmap blueBM; 947 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 948 SkBitmap redBM; 949 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 950 SkPaint semiTransparent; 951 semiTransparent.setAlpha(0x80); 952 953 SkPictureRecorder recorder; 954 SkCanvas* canvas = recorder.beginRecording(100, 100); 955 canvas->drawARGB(0, 0, 0, 0); 956 957 canvas->saveLayer(0, &semiTransparent); 958 canvas->drawBitmap(blueBM, 25, 25); 959 canvas->drawBitmap(redBM, 50, 50); 960 canvas->restore(); 961 962 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 963 964 // Now replay the picture back on another canvas 965 // and check a couple of its pixels. 966 SkBitmap replayBM; 967 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 968 SkCanvas replayCanvas(replayBM); 969 picture->playback(&replayCanvas); 970 replayCanvas.flush(); 971 972 // With the bug present, at (55, 55) we would get a fully opaque red 973 // intead of a dark red. 974 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 975 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 976} 977 978struct CountingBBH : public SkBBoxHierarchy { 979 mutable int searchCalls; 980 SkRect rootBound; 981 982 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 983 984 void search(const SkRect& query, SkTDArray<int>* results) const override { 985 this->searchCalls++; 986 } 987 988 void insert(const SkRect[], int) override {} 989 virtual size_t bytesUsed() const override { return 0; } 990 SkRect getRootBound() const override { return rootBound; } 991}; 992 993class SpoonFedBBHFactory : public SkBBHFactory { 994public: 995 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 996 SkBBoxHierarchy* operator()(const SkRect&) const override { 997 return SkRef(fBBH); 998 } 999private: 1000 SkBBoxHierarchy* fBBH; 1001}; 1002 1003// When the canvas clip covers the full picture, we don't need to call the BBH. 1004DEF_TEST(Picture_SkipBBH, r) { 1005 SkRect bound = SkRect::MakeWH(320, 240); 1006 CountingBBH bbh(bound); 1007 SpoonFedBBHFactory factory(&bbh); 1008 1009 SkPictureRecorder recorder; 1010 SkCanvas* c = recorder.beginRecording(bound, &factory); 1011 // Record a few ops so we don't hit a small- or empty- picture optimization. 1012 c->drawRect(bound, SkPaint()); 1013 c->drawRect(bound, SkPaint()); 1014 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1015 1016 SkCanvas big(640, 480), small(300, 200); 1017 1018 picture->playback(&big); 1019 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1020 1021 picture->playback(&small); 1022 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1023} 1024 1025DEF_TEST(Picture_BitmapLeak, r) { 1026 SkBitmap mut, immut; 1027 mut.allocN32Pixels(300, 200); 1028 immut.allocN32Pixels(300, 200); 1029 immut.setImmutable(); 1030 SkASSERT(!mut.isImmutable()); 1031 SkASSERT(immut.isImmutable()); 1032 1033 // No one can hold a ref on our pixels yet. 1034 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1035 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1036 1037 sk_sp<SkPicture> pic; 1038 { 1039 // we want the recorder to go out of scope before our subsequent checks, so we 1040 // place it inside local braces. 1041 SkPictureRecorder rec; 1042 SkCanvas* canvas = rec.beginRecording(1920, 1200); 1043 canvas->drawBitmap(mut, 0, 0); 1044 canvas->drawBitmap(immut, 800, 600); 1045 pic = rec.finishRecordingAsPicture(); 1046 } 1047 1048 // The picture shares the immutable pixels but copies the mutable ones. 1049 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1050 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 1051 1052 // When the picture goes away, it's just our bitmaps holding the refs. 1053 pic = nullptr; 1054 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1055 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1056} 1057 1058// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 1059DEF_TEST(Picture_getRecordingCanvas, r) { 1060 SkPictureRecorder rec; 1061 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1062 for (int i = 0; i < 3; i++) { 1063 rec.beginRecording(100, 100); 1064 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 1065 rec.finishRecordingAsPicture(); 1066 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1067 } 1068} 1069 1070DEF_TEST(MiniRecorderLeftHanging, r) { 1071 // Any shader or other ref-counted effect will do just fine here. 1072 SkPaint paint; 1073 paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); 1074 1075 SkMiniRecorder rec; 1076 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 1077 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 1078} 1079 1080DEF_TEST(Picture_preserveCullRect, r) { 1081 SkPictureRecorder recorder; 1082 1083 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 1084 c->clear(SK_ColorCYAN); 1085 1086 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1087 SkDynamicMemoryWStream wstream; 1088 picture->serialize(&wstream); 1089 1090 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream()); 1091 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream)); 1092 1093 REPORTER_ASSERT(r, deserializedPicture != nullptr); 1094 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 1095 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 1096 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 1097 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 1098} 1099 1100#if SK_SUPPORT_GPU 1101 1102DEF_TEST(PictureGpuAnalyzer, r) { 1103 SkPictureRecorder recorder; 1104 1105 { 1106 SkCanvas* canvas = recorder.beginRecording(10, 10); 1107 SkPaint paint; 1108 SkScalar intervals [] = { 10, 20 }; 1109 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); 1110 1111 for (int i = 0; i < 50; ++i) { 1112 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 1113 } 1114 } 1115 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture()); 1116 1117 SkPictureGpuAnalyzer analyzer; 1118 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1119 1120 analyzer.analyzePicture(vetoPicture.get()); 1121 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1122 1123 analyzer.reset(); 1124 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1125 1126 recorder.beginRecording(10, 10)->drawPicture(vetoPicture); 1127 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture()); 1128 1129 analyzer.analyzePicture(nestedVetoPicture.get()); 1130 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1131 1132 analyzer.reset(); 1133 1134 const SkPath convexClip = make_convex_path(); 1135 const SkPath concaveClip = make_concave_path(); 1136 for (int i = 0; i < 50; ++i) { 1137 analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, false); 1138 analyzer.analyzeClipPath(convexClip, SkCanvas::kIntersect_Op, true); 1139 analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, false); 1140 } 1141 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1142 1143 for (int i = 0; i < 50; ++i) { 1144 analyzer.analyzeClipPath(concaveClip, SkCanvas::kIntersect_Op, true); 1145 } 1146 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1147} 1148 1149#endif // SK_SUPPORT_GPU 1150 1151/////////////////////////////////////////////////////////////////////////////////////////////////// 1152 1153// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548 1154#if 0 1155static void empty_ops(SkCanvas* canvas) { 1156} 1157static void clip_ops(SkCanvas* canvas) { 1158 canvas->save(); 1159 canvas->clipRect(SkRect::MakeWH(20, 20)); 1160 canvas->restore(); 1161} 1162static void matrix_ops(SkCanvas* canvas) { 1163 canvas->save(); 1164 canvas->scale(2, 3); 1165 canvas->restore(); 1166} 1167static void matrixclip_ops(SkCanvas* canvas) { 1168 canvas->save(); 1169 canvas->scale(2, 3); 1170 canvas->clipRect(SkRect::MakeWH(20, 20)); 1171 canvas->restore(); 1172} 1173typedef void (*CanvasProc)(SkCanvas*); 1174 1175// Test the kReturnNullForEmpty_FinishFlag option when recording 1176// 1177DEF_TEST(Picture_RecordEmpty, r) { 1178 const SkRect cull = SkRect::MakeWH(100, 100); 1179 1180 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops }; 1181 1182 for (auto proc : procs) { 1183 { 1184 SkPictureRecorder rec; 1185 proc(rec.beginRecording(cull)); 1186 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0); 1187 REPORTER_ASSERT(r, pic.get()); 1188 REPORTER_ASSERT(r, pic->approximateOpCount() == 0); 1189 } 1190 { 1191 SkPictureRecorder rec; 1192 proc(rec.beginRecording(cull)); 1193 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture( 1194 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1195 REPORTER_ASSERT(r, !pic.get()); 1196 } 1197 { 1198 SkPictureRecorder rec; 1199 proc(rec.beginRecording(cull)); 1200 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0); 1201 REPORTER_ASSERT(r, dr.get()); 1202 } 1203 { 1204 SkPictureRecorder rec; 1205 proc(rec.beginRecording(cull)); 1206 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable( 1207 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1208 REPORTER_ASSERT(r, !dr.get()); 1209 } 1210 } 1211} 1212#endif 1213 1214