PictureTest.cpp revision 918e144408ba218df919528f8b48c544f4767883
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, kIntersect_SkClipOp, 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, kIntersect_SkClipOp, 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, kReplace_SkClipOp); 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 = canvas.getLocalClipBounds(); 421 422 canvas.drawPicture(picture); 423 424 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 425 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 426 427 SkRect afterClip = canvas.getLocalClipBounds(); 428 429 REPORTER_ASSERT(reporter, afterClip == beforeClip); 430} 431 432// Test out SkPictureRecorder::partialReplay 433DEF_TEST(PictureRecorder_replay, reporter) { 434 // check save/saveLayer state 435 { 436 SkPictureRecorder recorder; 437 438 SkCanvas* canvas = recorder.beginRecording(10, 10); 439 440 canvas->saveLayer(nullptr, nullptr); 441 442 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 443 444 // The extra save and restore comes from the Copy process. 445 check_save_state(reporter, copy.get(), 2, 1, 3); 446 447 canvas->saveLayer(nullptr, nullptr); 448 449 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 450 451 check_save_state(reporter, final.get(), 1, 2, 3); 452 453 // The copy shouldn't pick up any operations added after it was made 454 check_save_state(reporter, copy.get(), 2, 1, 3); 455 } 456 457 // (partially) check leakage of draw ops 458 { 459 SkPictureRecorder recorder; 460 461 SkCanvas* canvas = recorder.beginRecording(10, 10); 462 463 SkRect r = SkRect::MakeWH(5, 5); 464 SkPaint p; 465 466 canvas->drawRect(r, p); 467 468 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 469 470 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 471 472 SkBitmap bm; 473 make_bm(&bm, 10, 10, SK_ColorRED, true); 474 475 r.offset(5.0f, 5.0f); 476 canvas->drawBitmapRect(bm, r, nullptr); 477 478 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 479 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); 480 481 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); 482 483 // The snapshot shouldn't pick up any operations added after it was made 484 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 485 } 486 487 // Recreate the Android partialReplay test case 488 { 489 SkPictureRecorder recorder; 490 491 SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0); 492 create_imbalance(canvas); 493 494 int expectedSaveCount = canvas->getSaveCount(); 495 496 sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 497 check_balance(reporter, copy.get()); 498 499 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 500 501 // End the recording of source to test the picture finalization 502 // process isn't complicated by the partialReplay step 503 sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); 504 } 505} 506 507static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 508 SkCanvas testCanvas(100, 100); 509 set_canvas_to_save_count_4(&testCanvas); 510 511 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 512 513 SkPaint paint; 514 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 515 516 SkPictureRecorder recorder; 517 518 { 519 // Create picture with 2 unbalanced saves 520 SkCanvas* canvas = recorder.beginRecording(100, 100); 521 canvas->save(); 522 canvas->translate(10, 10); 523 canvas->drawRect(rect, paint); 524 canvas->save(); 525 canvas->translate(10, 10); 526 canvas->drawRect(rect, paint); 527 sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture()); 528 529 testCanvas.drawPicture(extraSavePicture); 530 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 531 } 532 533 set_canvas_to_save_count_4(&testCanvas); 534 535 { 536 // Create picture with 2 unbalanced restores 537 SkCanvas* canvas = recorder.beginRecording(100, 100); 538 canvas->save(); 539 canvas->translate(10, 10); 540 canvas->drawRect(rect, paint); 541 canvas->save(); 542 canvas->translate(10, 10); 543 canvas->drawRect(rect, paint); 544 canvas->restore(); 545 canvas->restore(); 546 canvas->restore(); 547 canvas->restore(); 548 sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture()); 549 550 testCanvas.drawPicture(extraRestorePicture); 551 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 552 } 553 554 set_canvas_to_save_count_4(&testCanvas); 555 556 { 557 SkCanvas* canvas = recorder.beginRecording(100, 100); 558 canvas->translate(10, 10); 559 canvas->drawRect(rect, paint); 560 sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture()); 561 562 testCanvas.drawPicture(noSavePicture); 563 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 564 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 565 } 566} 567 568static void test_peephole() { 569 SkRandom rand; 570 571 SkPictureRecorder recorder; 572 573 for (int j = 0; j < 100; j++) { 574 SkRandom rand2(rand); // remember the seed 575 576 SkCanvas* canvas = recorder.beginRecording(100, 100); 577 578 for (int i = 0; i < 1000; ++i) { 579 rand_op(canvas, rand); 580 } 581 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 582 583 rand = rand2; 584 } 585 586 { 587 SkCanvas* canvas = recorder.beginRecording(100, 100); 588 SkRect rect = SkRect::MakeWH(50, 50); 589 590 for (int i = 0; i < 100; ++i) { 591 canvas->save(); 592 } 593 while (canvas->getSaveCount() > 1) { 594 canvas->clipRect(rect); 595 canvas->restore(); 596 } 597 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 598 } 599} 600 601#ifndef SK_DEBUG 602// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 603// should never do this. 604static void test_bad_bitmap() { 605 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 606 // fail. 607 SkBitmap bm; 608 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 609 SkPictureRecorder recorder; 610 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 611 recordingCanvas->drawBitmap(bm, 0, 0); 612 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 613 614 SkCanvas canvas; 615 canvas.drawPicture(picture); 616} 617#endif 618 619static void test_clip_bound_opt(skiatest::Reporter* reporter) { 620 // Test for crbug.com/229011 621 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 622 SkIntToScalar(2), SkIntToScalar(2)); 623 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 624 SkIntToScalar(1), SkIntToScalar(1)); 625 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 626 SkIntToScalar(1), SkIntToScalar(1)); 627 628 SkPath invPath; 629 invPath.addOval(rect1); 630 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 631 SkPath path; 632 path.addOval(rect2); 633 SkPath path2; 634 path2.addOval(rect3); 635 SkIRect clipBounds; 636 SkPictureRecorder recorder; 637 638 // Testing conservative-raster-clip that is enabled by PictureRecord 639 { 640 SkCanvas* canvas = recorder.beginRecording(10, 10); 641 canvas->clipPath(invPath); 642 clipBounds = canvas->getDeviceClipBounds(); 643 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 644 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 645 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 646 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 647 } 648 { 649 SkCanvas* canvas = recorder.beginRecording(10, 10); 650 canvas->clipPath(path); 651 canvas->clipPath(invPath); 652 clipBounds = canvas->getDeviceClipBounds(); 653 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 654 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 655 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 656 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 657 } 658 { 659 SkCanvas* canvas = recorder.beginRecording(10, 10); 660 canvas->clipPath(path); 661 canvas->clipPath(invPath, kUnion_SkClipOp); 662 clipBounds = canvas->getDeviceClipBounds(); 663 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 664 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 665 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 666 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 667 } 668 { 669 SkCanvas* canvas = recorder.beginRecording(10, 10); 670 canvas->clipPath(path, kDifference_SkClipOp); 671 clipBounds = canvas->getDeviceClipBounds(); 672 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 673 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 674 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 675 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 676 } 677 { 678 SkCanvas* canvas = recorder.beginRecording(10, 10); 679 canvas->clipPath(path, kReverseDifference_SkClipOp); 680 clipBounds = canvas->getDeviceClipBounds(); 681 // True clip is actually empty in this case, but the best 682 // determination we can make using only bounds as input is that the 683 // clip is included in the bounds of 'path'. 684 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 685 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 686 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 687 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 688 } 689 { 690 SkCanvas* canvas = recorder.beginRecording(10, 10); 691 canvas->clipPath(path, kIntersect_SkClipOp); 692 canvas->clipPath(path2, kXOR_SkClipOp); 693 clipBounds = canvas->getDeviceClipBounds(); 694 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 695 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 696 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 697 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 698 } 699} 700 701static void test_cull_rect_reset(skiatest::Reporter* reporter) { 702 SkPictureRecorder recorder; 703 SkRect bounds = SkRect::MakeWH(10, 10); 704 SkRTreeFactory factory; 705 SkCanvas* canvas = recorder.beginRecording(bounds, &factory); 706 bounds = SkRect::MakeWH(100, 100); 707 SkPaint paint; 708 canvas->drawRect(bounds, paint); 709 canvas->drawRect(bounds, paint); 710 sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds)); 711 const SkBigPicture* picture = p->asSkBigPicture(); 712 REPORTER_ASSERT(reporter, picture); 713 714 SkRect finalCullRect = picture->cullRect(); 715 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft); 716 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop); 717 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom); 718 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight); 719 720 const SkBBoxHierarchy* pictureBBH = picture->bbh(); 721 SkRect bbhCullRect = pictureBBH->getRootBound(); 722 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft); 723 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop); 724 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom); 725 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight); 726} 727 728 729/** 730 * A canvas that records the number of clip commands. 731 */ 732class ClipCountingCanvas : public SkCanvas { 733public: 734 ClipCountingCanvas(int width, int height) 735 : INHERITED(width, height) 736 , fClipCount(0){ 737 } 738 739 void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override { 740 fClipCount += 1; 741 this->INHERITED::onClipRect(r, op, edgeStyle); 742 } 743 744 void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override { 745 fClipCount += 1; 746 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 747 } 748 749 void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override { 750 fClipCount += 1; 751 this->INHERITED::onClipPath(path, op, edgeStyle); 752 } 753 754 void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override { 755 fClipCount += 1; 756 this->INHERITED::onClipRegion(deviceRgn, op); 757 } 758 759 unsigned getClipCount() const { return fClipCount; } 760 761private: 762 unsigned fClipCount; 763 764 typedef SkCanvas INHERITED; 765}; 766 767static void test_clip_expansion(skiatest::Reporter* reporter) { 768 SkPictureRecorder recorder; 769 SkCanvas* canvas = recorder.beginRecording(10, 10); 770 771 canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp); 772 // The following expanding clip should not be skipped. 773 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp); 774 // Draw something so the optimizer doesn't just fold the world. 775 SkPaint p; 776 p.setColor(SK_ColorBLUE); 777 canvas->drawPaint(p); 778 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 779 780 ClipCountingCanvas testCanvas(10, 10); 781 picture->playback(&testCanvas); 782 783 // Both clips should be present on playback. 784 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 785} 786 787static void test_hierarchical(skiatest::Reporter* reporter) { 788 SkBitmap bm; 789 make_bm(&bm, 10, 10, SK_ColorRED, true); 790 791 SkPictureRecorder recorder; 792 793 recorder.beginRecording(10, 10); 794 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture()); 795 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 796 797 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 798 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture()); 799 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 800 801 { 802 SkCanvas* canvas = recorder.beginRecording(10, 10); 803 canvas->drawPicture(childPlain); 804 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture()); 805 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 806 } 807 { 808 SkCanvas* canvas = recorder.beginRecording(10, 10); 809 canvas->drawPicture(childWithBitmap); 810 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture()); 811 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 812 } 813 { 814 SkCanvas* canvas = recorder.beginRecording(10, 10); 815 canvas->drawBitmap(bm, 0, 0); 816 canvas->drawPicture(childPlain); 817 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture()); 818 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 819 } 820 { 821 SkCanvas* canvas = recorder.beginRecording(10, 10); 822 canvas->drawBitmap(bm, 0, 0); 823 canvas->drawPicture(childWithBitmap); 824 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture()); 825 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 826 } 827} 828 829static void test_gen_id(skiatest::Reporter* reporter) { 830 831 SkPictureRecorder recorder; 832 recorder.beginRecording(0, 0); 833 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture()); 834 835 // Empty pictures should still have a valid ID 836 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 837 838 SkCanvas* canvas = recorder.beginRecording(1, 1); 839 canvas->drawARGB(255, 255, 255, 255); 840 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture()); 841 // picture should have a non-zero id after recording 842 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 843 844 // both pictures should have different ids 845 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 846} 847 848static void test_typeface(skiatest::Reporter* reporter) { 849 SkPictureRecorder recorder; 850 SkCanvas* canvas = recorder.beginRecording(10, 10); 851 SkPaint paint; 852 paint.setTypeface(SkTypeface::MakeFromName("Arial", 853 SkFontStyle::FromOldStyle(SkTypeface::kItalic))); 854 canvas->drawText("Q", 1, 0, 10, paint); 855 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 856 SkDynamicMemoryWStream stream; 857 picture->serialize(&stream); 858} 859 860DEF_TEST(Picture, reporter) { 861 test_typeface(reporter); 862#ifdef SK_DEBUG 863 test_deleting_empty_picture(); 864 test_serializing_empty_picture(); 865#else 866 test_bad_bitmap(); 867#endif 868 test_unbalanced_save_restores(reporter); 869 test_peephole(); 870#if SK_SUPPORT_GPU 871 test_gpu_veto(reporter); 872#endif 873 test_images_are_found_by_willPlayBackBitmaps(reporter); 874 test_analysis(reporter); 875 test_clip_bound_opt(reporter); 876 test_clip_expansion(reporter); 877 test_hierarchical(reporter); 878 test_gen_id(reporter); 879 test_cull_rect_reset(reporter); 880} 881 882static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 883 const SkPaint paint; 884 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 885 const SkIRect irect = { 2, 2, 3, 3 }; 886 int divs[] = { 2, 3 }; 887 SkCanvas::Lattice lattice; 888 lattice.fXCount = lattice.fYCount = 2; 889 lattice.fXDivs = lattice.fYDivs = divs; 890 891 // Don't care what these record, as long as they're legal. 892 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 893 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); 894 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 895 canvas->drawBitmap(bitmap, 1, 1); // drawSprite 896 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); 897} 898 899static void test_draw_bitmaps(SkCanvas* canvas) { 900 SkBitmap empty; 901 draw_bitmaps(empty, canvas); 902 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 903 draw_bitmaps(empty, canvas); 904} 905 906DEF_TEST(Picture_EmptyBitmap, r) { 907 SkPictureRecorder recorder; 908 test_draw_bitmaps(recorder.beginRecording(10, 10)); 909 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 910} 911 912DEF_TEST(Canvas_EmptyBitmap, r) { 913 SkBitmap dst; 914 dst.allocN32Pixels(10, 10); 915 SkCanvas canvas(dst); 916 917 test_draw_bitmaps(&canvas); 918} 919 920DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 921 // This test is from crbug.com/344987. 922 // The commands are: 923 // saveLayer with paint that modifies alpha 924 // drawBitmapRect 925 // drawBitmapRect 926 // restore 927 // The bug was that this structure was modified so that: 928 // - The saveLayer and restore were eliminated 929 // - The alpha was only applied to the first drawBitmapRectToRect 930 931 // This test draws blue and red squares inside a 50% transparent 932 // layer. Both colours should show up muted. 933 // When the bug is present, the red square (the second bitmap) 934 // shows upwith full opacity. 935 936 SkBitmap blueBM; 937 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 938 SkBitmap redBM; 939 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 940 SkPaint semiTransparent; 941 semiTransparent.setAlpha(0x80); 942 943 SkPictureRecorder recorder; 944 SkCanvas* canvas = recorder.beginRecording(100, 100); 945 canvas->drawARGB(0, 0, 0, 0); 946 947 canvas->saveLayer(0, &semiTransparent); 948 canvas->drawBitmap(blueBM, 25, 25); 949 canvas->drawBitmap(redBM, 50, 50); 950 canvas->restore(); 951 952 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 953 954 // Now replay the picture back on another canvas 955 // and check a couple of its pixels. 956 SkBitmap replayBM; 957 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 958 SkCanvas replayCanvas(replayBM); 959 picture->playback(&replayCanvas); 960 replayCanvas.flush(); 961 962 // With the bug present, at (55, 55) we would get a fully opaque red 963 // intead of a dark red. 964 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 965 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 966} 967 968struct CountingBBH : public SkBBoxHierarchy { 969 mutable int searchCalls; 970 SkRect rootBound; 971 972 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 973 974 void search(const SkRect& query, SkTDArray<int>* results) const override { 975 this->searchCalls++; 976 } 977 978 void insert(const SkRect[], int) override {} 979 virtual size_t bytesUsed() const override { return 0; } 980 SkRect getRootBound() const override { return rootBound; } 981}; 982 983class SpoonFedBBHFactory : public SkBBHFactory { 984public: 985 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 986 SkBBoxHierarchy* operator()(const SkRect&) const override { 987 return SkRef(fBBH); 988 } 989private: 990 SkBBoxHierarchy* fBBH; 991}; 992 993// When the canvas clip covers the full picture, we don't need to call the BBH. 994DEF_TEST(Picture_SkipBBH, r) { 995 SkRect bound = SkRect::MakeWH(320, 240); 996 CountingBBH bbh(bound); 997 SpoonFedBBHFactory factory(&bbh); 998 999 SkPictureRecorder recorder; 1000 SkCanvas* c = recorder.beginRecording(bound, &factory); 1001 // Record a few ops so we don't hit a small- or empty- picture optimization. 1002 c->drawRect(bound, SkPaint()); 1003 c->drawRect(bound, SkPaint()); 1004 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1005 1006 SkCanvas big(640, 480), small(300, 200); 1007 1008 picture->playback(&big); 1009 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1010 1011 picture->playback(&small); 1012 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1013} 1014 1015DEF_TEST(Picture_BitmapLeak, r) { 1016 SkBitmap mut, immut; 1017 mut.allocN32Pixels(300, 200); 1018 immut.allocN32Pixels(300, 200); 1019 immut.setImmutable(); 1020 SkASSERT(!mut.isImmutable()); 1021 SkASSERT(immut.isImmutable()); 1022 1023 // No one can hold a ref on our pixels yet. 1024 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1025 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1026 1027 sk_sp<SkPicture> pic; 1028 { 1029 // we want the recorder to go out of scope before our subsequent checks, so we 1030 // place it inside local braces. 1031 SkPictureRecorder rec; 1032 SkCanvas* canvas = rec.beginRecording(1920, 1200); 1033 canvas->drawBitmap(mut, 0, 0); 1034 canvas->drawBitmap(immut, 800, 600); 1035 pic = rec.finishRecordingAsPicture(); 1036 } 1037 1038 // The picture shares the immutable pixels but copies the mutable ones. 1039 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1040 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 1041 1042 // When the picture goes away, it's just our bitmaps holding the refs. 1043 pic = nullptr; 1044 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1045 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1046} 1047 1048// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 1049DEF_TEST(Picture_getRecordingCanvas, r) { 1050 SkPictureRecorder rec; 1051 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1052 for (int i = 0; i < 3; i++) { 1053 rec.beginRecording(100, 100); 1054 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 1055 rec.finishRecordingAsPicture(); 1056 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1057 } 1058} 1059 1060DEF_TEST(MiniRecorderLeftHanging, r) { 1061 // Any shader or other ref-counted effect will do just fine here. 1062 SkPaint paint; 1063 paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); 1064 1065 SkMiniRecorder rec; 1066 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 1067 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 1068} 1069 1070DEF_TEST(Picture_preserveCullRect, r) { 1071 SkPictureRecorder recorder; 1072 1073 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 1074 c->clear(SK_ColorCYAN); 1075 1076 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1077 SkDynamicMemoryWStream wstream; 1078 picture->serialize(&wstream); 1079 1080 std::unique_ptr<SkStream> rstream(wstream.detachAsStream()); 1081 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get())); 1082 1083 REPORTER_ASSERT(r, deserializedPicture != nullptr); 1084 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 1085 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 1086 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 1087 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 1088} 1089 1090#if SK_SUPPORT_GPU 1091 1092DEF_TEST(PictureGpuAnalyzer, r) { 1093 SkPictureRecorder recorder; 1094 1095 { 1096 SkCanvas* canvas = recorder.beginRecording(10, 10); 1097 SkPaint paint; 1098 SkScalar intervals [] = { 10, 20 }; 1099 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); 1100 1101 for (int i = 0; i < 50; ++i) { 1102 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 1103 } 1104 } 1105 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture()); 1106 1107 SkPictureGpuAnalyzer analyzer; 1108 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1109 1110 analyzer.analyzePicture(vetoPicture.get()); 1111 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1112 1113 analyzer.reset(); 1114 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1115 1116 recorder.beginRecording(10, 10)->drawPicture(vetoPicture); 1117 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture()); 1118 1119 analyzer.analyzePicture(nestedVetoPicture.get()); 1120 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1121 1122 analyzer.reset(); 1123 1124 const SkPath convexClip = make_convex_path(); 1125 const SkPath concaveClip = make_concave_path(); 1126 for (int i = 0; i < 50; ++i) { 1127 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false); 1128 analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true); 1129 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false); 1130 } 1131 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1132 1133 for (int i = 0; i < 50; ++i) { 1134 analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true); 1135 } 1136 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1137} 1138 1139#endif // SK_SUPPORT_GPU 1140 1141/////////////////////////////////////////////////////////////////////////////////////////////////// 1142 1143// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548 1144#if 0 1145static void empty_ops(SkCanvas* canvas) { 1146} 1147static void clip_ops(SkCanvas* canvas) { 1148 canvas->save(); 1149 canvas->clipRect(SkRect::MakeWH(20, 20)); 1150 canvas->restore(); 1151} 1152static void matrix_ops(SkCanvas* canvas) { 1153 canvas->save(); 1154 canvas->scale(2, 3); 1155 canvas->restore(); 1156} 1157static void matrixclip_ops(SkCanvas* canvas) { 1158 canvas->save(); 1159 canvas->scale(2, 3); 1160 canvas->clipRect(SkRect::MakeWH(20, 20)); 1161 canvas->restore(); 1162} 1163typedef void (*CanvasProc)(SkCanvas*); 1164 1165// Test the kReturnNullForEmpty_FinishFlag option when recording 1166// 1167DEF_TEST(Picture_RecordEmpty, r) { 1168 const SkRect cull = SkRect::MakeWH(100, 100); 1169 1170 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops }; 1171 1172 for (auto proc : procs) { 1173 { 1174 SkPictureRecorder rec; 1175 proc(rec.beginRecording(cull)); 1176 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0); 1177 REPORTER_ASSERT(r, pic.get()); 1178 REPORTER_ASSERT(r, pic->approximateOpCount() == 0); 1179 } 1180 { 1181 SkPictureRecorder rec; 1182 proc(rec.beginRecording(cull)); 1183 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture( 1184 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1185 REPORTER_ASSERT(r, !pic.get()); 1186 } 1187 { 1188 SkPictureRecorder rec; 1189 proc(rec.beginRecording(cull)); 1190 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0); 1191 REPORTER_ASSERT(r, dr.get()); 1192 } 1193 { 1194 SkPictureRecorder rec; 1195 proc(rec.beginRecording(cull)); 1196 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable( 1197 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1198 REPORTER_ASSERT(r, !dr.get()); 1199 } 1200 } 1201} 1202#endif 1203 1204