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