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