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