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