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