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