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