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