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