PictureTest.cpp revision c573a40ed5024b463e47088d307e3164a486dba5
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()->isABitmap()); 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, SkRegion::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, SkRegion::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, SkRegion::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 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.copyToData(); 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 SkAutoDataUnref data(wStream.copyToData()); 674 675 SkBitmap bm; 676 bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &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 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 683 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 684 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 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(picture1); 693 SkClearLastError(); 694 sk_sp<SkPicture> pictureFromStream(SkPicture::MakeFromStream(&pictureStream, nullptr)); 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, SkRegion::kIntersect_Op); 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, SkRegion::kIntersect_Op); 751 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 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, SkRegion::kIntersect_Op); 762 canvas->clipPath(invPath, SkRegion::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, SkRegion::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, SkRegion::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, SkRegion::kIntersect_Op); 796 canvas->clipPath(path2, SkRegion::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 virtual void onClipRect(const SkRect& r, 845 SkRegion::Op op, 846 ClipEdgeStyle edgeStyle) override { 847 fClipCount += 1; 848 this->INHERITED::onClipRect(r, op, edgeStyle); 849 } 850 851 virtual void onClipRRect(const SkRRect& rrect, 852 SkRegion::Op op, 853 ClipEdgeStyle edgeStyle)override { 854 fClipCount += 1; 855 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 856 } 857 858 virtual void onClipPath(const SkPath& path, 859 SkRegion::Op op, 860 ClipEdgeStyle edgeStyle) override { 861 fClipCount += 1; 862 this->INHERITED::onClipPath(path, op, edgeStyle); 863 } 864 865 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override { 866 fClipCount += 1; 867 this->INHERITED::onClipRegion(deviceRgn, op); 868 } 869 870 unsigned getClipCount() const { return fClipCount; } 871 872private: 873 unsigned fClipCount; 874 875 typedef SkCanvas INHERITED; 876}; 877 878static void test_clip_expansion(skiatest::Reporter* reporter) { 879 SkPictureRecorder recorder; 880 SkCanvas* canvas = recorder.beginRecording(10, 10); 881 882 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 883 // The following expanding clip should not be skipped. 884 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 885 // Draw something so the optimizer doesn't just fold the world. 886 SkPaint p; 887 p.setColor(SK_ColorBLUE); 888 canvas->drawPaint(p); 889 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 890 891 ClipCountingCanvas testCanvas(10, 10); 892 picture->playback(&testCanvas); 893 894 // Both clips should be present on playback. 895 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 896} 897 898static void test_hierarchical(skiatest::Reporter* reporter) { 899 SkBitmap bm; 900 make_bm(&bm, 10, 10, SK_ColorRED, true); 901 902 SkPictureRecorder recorder; 903 904 recorder.beginRecording(10, 10); 905 sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture()); 906 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 907 908 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 909 sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture()); 910 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 911 912 { 913 SkCanvas* canvas = recorder.beginRecording(10, 10); 914 canvas->drawPicture(childPlain); 915 sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture()); 916 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 917 } 918 { 919 SkCanvas* canvas = recorder.beginRecording(10, 10); 920 canvas->drawPicture(childWithBitmap); 921 sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture()); 922 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 923 } 924 { 925 SkCanvas* canvas = recorder.beginRecording(10, 10); 926 canvas->drawBitmap(bm, 0, 0); 927 canvas->drawPicture(childPlain); 928 sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture()); 929 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 930 } 931 { 932 SkCanvas* canvas = recorder.beginRecording(10, 10); 933 canvas->drawBitmap(bm, 0, 0); 934 canvas->drawPicture(childWithBitmap); 935 sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture()); 936 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 937 } 938} 939 940static void test_gen_id(skiatest::Reporter* reporter) { 941 942 SkPictureRecorder recorder; 943 recorder.beginRecording(0, 0); 944 sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture()); 945 946 // Empty pictures should still have a valid ID 947 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 948 949 SkCanvas* canvas = recorder.beginRecording(1, 1); 950 canvas->drawARGB(255, 255, 255, 255); 951 sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture()); 952 // picture should have a non-zero id after recording 953 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 954 955 // both pictures should have different ids 956 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 957} 958 959static void test_typeface(skiatest::Reporter* reporter) { 960 SkPictureRecorder recorder; 961 SkCanvas* canvas = recorder.beginRecording(10, 10); 962 SkPaint paint; 963 paint.setTypeface(SkTypeface::MakeFromName("Arial", 964 SkFontStyle::FromOldStyle(SkTypeface::kItalic))); 965 canvas->drawText("Q", 1, 0, 10, paint); 966 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 967 SkDynamicMemoryWStream stream; 968 picture->serialize(&stream); 969} 970 971DEF_TEST(Picture, reporter) { 972 test_typeface(reporter); 973#ifdef SK_DEBUG 974 test_deleting_empty_picture(); 975 test_serializing_empty_picture(); 976#else 977 test_bad_bitmap(); 978#endif 979 test_unbalanced_save_restores(reporter); 980 test_peephole(); 981#if SK_SUPPORT_GPU 982 test_gpu_veto(reporter); 983#endif 984 test_images_are_found_by_willPlayBackBitmaps(reporter); 985 test_analysis(reporter); 986 test_clip_bound_opt(reporter); 987 test_clip_expansion(reporter); 988 test_hierarchical(reporter); 989 test_gen_id(reporter); 990 test_cull_rect_reset(reporter); 991} 992 993static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 994 const SkPaint paint; 995 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 996 const SkIRect irect = { 2, 2, 3, 3 }; 997 int divs[] = { 2, 3 }; 998 SkCanvas::Lattice lattice; 999 lattice.fXCount = lattice.fYCount = 2; 1000 lattice.fXDivs = lattice.fYDivs = divs; 1001 1002 // Don't care what these record, as long as they're legal. 1003 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1004 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); 1005 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1006 canvas->drawBitmap(bitmap, 1, 1); // drawSprite 1007 canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); 1008} 1009 1010static void test_draw_bitmaps(SkCanvas* canvas) { 1011 SkBitmap empty; 1012 draw_bitmaps(empty, canvas); 1013 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1014 draw_bitmaps(empty, canvas); 1015} 1016 1017DEF_TEST(Picture_EmptyBitmap, r) { 1018 SkPictureRecorder recorder; 1019 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1020 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1021} 1022 1023DEF_TEST(Canvas_EmptyBitmap, r) { 1024 SkBitmap dst; 1025 dst.allocN32Pixels(10, 10); 1026 SkCanvas canvas(dst); 1027 1028 test_draw_bitmaps(&canvas); 1029} 1030 1031DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1032 // This test is from crbug.com/344987. 1033 // The commands are: 1034 // saveLayer with paint that modifies alpha 1035 // drawBitmapRect 1036 // drawBitmapRect 1037 // restore 1038 // The bug was that this structure was modified so that: 1039 // - The saveLayer and restore were eliminated 1040 // - The alpha was only applied to the first drawBitmapRectToRect 1041 1042 // This test draws blue and red squares inside a 50% transparent 1043 // layer. Both colours should show up muted. 1044 // When the bug is present, the red square (the second bitmap) 1045 // shows upwith full opacity. 1046 1047 SkBitmap blueBM; 1048 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1049 SkBitmap redBM; 1050 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1051 SkPaint semiTransparent; 1052 semiTransparent.setAlpha(0x80); 1053 1054 SkPictureRecorder recorder; 1055 SkCanvas* canvas = recorder.beginRecording(100, 100); 1056 canvas->drawARGB(0, 0, 0, 0); 1057 1058 canvas->saveLayer(0, &semiTransparent); 1059 canvas->drawBitmap(blueBM, 25, 25); 1060 canvas->drawBitmap(redBM, 50, 50); 1061 canvas->restore(); 1062 1063 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1064 1065 // Now replay the picture back on another canvas 1066 // and check a couple of its pixels. 1067 SkBitmap replayBM; 1068 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1069 SkCanvas replayCanvas(replayBM); 1070 picture->playback(&replayCanvas); 1071 replayCanvas.flush(); 1072 1073 // With the bug present, at (55, 55) we would get a fully opaque red 1074 // intead of a dark red. 1075 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1076 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1077} 1078 1079struct CountingBBH : public SkBBoxHierarchy { 1080 mutable int searchCalls; 1081 SkRect rootBound; 1082 1083 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 1084 1085 void search(const SkRect& query, SkTDArray<int>* results) const override { 1086 this->searchCalls++; 1087 } 1088 1089 void insert(const SkRect[], int) override {} 1090 virtual size_t bytesUsed() const override { return 0; } 1091 SkRect getRootBound() const override { return rootBound; } 1092}; 1093 1094class SpoonFedBBHFactory : public SkBBHFactory { 1095public: 1096 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1097 SkBBoxHierarchy* operator()(const SkRect&) const override { 1098 return SkRef(fBBH); 1099 } 1100private: 1101 SkBBoxHierarchy* fBBH; 1102}; 1103 1104// When the canvas clip covers the full picture, we don't need to call the BBH. 1105DEF_TEST(Picture_SkipBBH, r) { 1106 SkRect bound = SkRect::MakeWH(320, 240); 1107 CountingBBH bbh(bound); 1108 SpoonFedBBHFactory factory(&bbh); 1109 1110 SkPictureRecorder recorder; 1111 SkCanvas* c = recorder.beginRecording(bound, &factory); 1112 // Record a few ops so we don't hit a small- or empty- picture optimization. 1113 c->drawRect(bound, SkPaint()); 1114 c->drawRect(bound, SkPaint()); 1115 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1116 1117 SkCanvas big(640, 480), small(300, 200); 1118 1119 picture->playback(&big); 1120 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1121 1122 picture->playback(&small); 1123 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1124} 1125 1126DEF_TEST(Picture_BitmapLeak, r) { 1127 SkBitmap mut, immut; 1128 mut.allocN32Pixels(300, 200); 1129 immut.allocN32Pixels(300, 200); 1130 immut.setImmutable(); 1131 SkASSERT(!mut.isImmutable()); 1132 SkASSERT(immut.isImmutable()); 1133 1134 // No one can hold a ref on our pixels yet. 1135 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1136 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1137 1138 sk_sp<SkPicture> pic; 1139 { 1140 // we want the recorder to go out of scope before our subsequent checks, so we 1141 // place it inside local braces. 1142 SkPictureRecorder rec; 1143 SkCanvas* canvas = rec.beginRecording(1920, 1200); 1144 canvas->drawBitmap(mut, 0, 0); 1145 canvas->drawBitmap(immut, 800, 600); 1146 pic = rec.finishRecordingAsPicture(); 1147 } 1148 1149 // The picture shares the immutable pixels but copies the mutable ones. 1150 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1151 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 1152 1153 // When the picture goes away, it's just our bitmaps holding the refs. 1154 pic = nullptr; 1155 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1156 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1157} 1158 1159// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 1160DEF_TEST(Picture_getRecordingCanvas, r) { 1161 SkPictureRecorder rec; 1162 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1163 for (int i = 0; i < 3; i++) { 1164 rec.beginRecording(100, 100); 1165 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 1166 rec.finishRecordingAsPicture(); 1167 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1168 } 1169} 1170 1171DEF_TEST(MiniRecorderLeftHanging, r) { 1172 // Any shader or other ref-counted effect will do just fine here. 1173 SkPaint paint; 1174 paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); 1175 1176 SkMiniRecorder rec; 1177 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 1178 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 1179} 1180 1181DEF_TEST(Picture_preserveCullRect, r) { 1182 SkPictureRecorder recorder; 1183 1184 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 1185 c->clear(SK_ColorCYAN); 1186 1187 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 1188 SkDynamicMemoryWStream wstream; 1189 picture->serialize(&wstream); 1190 1191 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream()); 1192 sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream)); 1193 1194 REPORTER_ASSERT(r, deserializedPicture != nullptr); 1195 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 1196 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 1197 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 1198 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 1199} 1200 1201#if SK_SUPPORT_GPU 1202 1203DEF_TEST(PictureGpuAnalyzer, r) { 1204 SkPictureRecorder recorder; 1205 1206 { 1207 SkCanvas* canvas = recorder.beginRecording(10, 10); 1208 SkPaint paint; 1209 SkScalar intervals [] = { 10, 20 }; 1210 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); 1211 1212 for (int i = 0; i < 50; ++i) { 1213 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 1214 } 1215 } 1216 sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture()); 1217 1218 SkPictureGpuAnalyzer analyzer; 1219 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1220 1221 analyzer.analyzePicture(vetoPicture.get()); 1222 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1223 1224 analyzer.reset(); 1225 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1226 1227 recorder.beginRecording(10, 10)->drawPicture(vetoPicture); 1228 sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture()); 1229 1230 analyzer.analyzePicture(nestedVetoPicture.get()); 1231 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1232 1233 analyzer.reset(); 1234 1235 const SkPath convexClip = make_convex_path(); 1236 const SkPath concaveClip = make_concave_path(); 1237 for (int i = 0; i < 50; ++i) { 1238 analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, false); 1239 analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, true); 1240 analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, false); 1241 } 1242 REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); 1243 1244 for (int i = 0; i < 50; ++i) { 1245 analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, true); 1246 } 1247 REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); 1248} 1249 1250#endif // SK_SUPPORT_GPU 1251 1252/////////////////////////////////////////////////////////////////////////////////////////////////// 1253 1254// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548 1255#if 0 1256static void empty_ops(SkCanvas* canvas) { 1257} 1258static void clip_ops(SkCanvas* canvas) { 1259 canvas->save(); 1260 canvas->clipRect(SkRect::MakeWH(20, 20)); 1261 canvas->restore(); 1262} 1263static void matrix_ops(SkCanvas* canvas) { 1264 canvas->save(); 1265 canvas->scale(2, 3); 1266 canvas->restore(); 1267} 1268static void matrixclip_ops(SkCanvas* canvas) { 1269 canvas->save(); 1270 canvas->scale(2, 3); 1271 canvas->clipRect(SkRect::MakeWH(20, 20)); 1272 canvas->restore(); 1273} 1274typedef void (*CanvasProc)(SkCanvas*); 1275 1276// Test the kReturnNullForEmpty_FinishFlag option when recording 1277// 1278DEF_TEST(Picture_RecordEmpty, r) { 1279 const SkRect cull = SkRect::MakeWH(100, 100); 1280 1281 CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops }; 1282 1283 for (auto proc : procs) { 1284 { 1285 SkPictureRecorder rec; 1286 proc(rec.beginRecording(cull)); 1287 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0); 1288 REPORTER_ASSERT(r, pic.get()); 1289 REPORTER_ASSERT(r, pic->approximateOpCount() == 0); 1290 } 1291 { 1292 SkPictureRecorder rec; 1293 proc(rec.beginRecording(cull)); 1294 sk_sp<SkPicture> pic = rec.finishRecordingAsPicture( 1295 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1296 REPORTER_ASSERT(r, !pic.get()); 1297 } 1298 { 1299 SkPictureRecorder rec; 1300 proc(rec.beginRecording(cull)); 1301 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0); 1302 REPORTER_ASSERT(r, dr.get()); 1303 } 1304 { 1305 SkPictureRecorder rec; 1306 proc(rec.beginRecording(cull)); 1307 sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable( 1308 SkPictureRecorder::kReturnNullForEmpty_FinishFlag); 1309 REPORTER_ASSERT(r, !dr.get()); 1310 } 1311 } 1312} 1313#endif 1314 1315