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