PictureTest.cpp revision 9db912c2ac2ab53bc24f2d50a3e5a80162051dcc
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->drawBitmapRectToRect(bm, NULL, r); 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 987/** 988 * A canvas that records the number of clip commands. 989 */ 990class ClipCountingCanvas : public SkCanvas { 991public: 992 ClipCountingCanvas(int width, int height) 993 : INHERITED(width, height) 994 , fClipCount(0){ 995 } 996 997 virtual void onClipRect(const SkRect& r, 998 SkRegion::Op op, 999 ClipEdgeStyle edgeStyle) override { 1000 fClipCount += 1; 1001 this->INHERITED::onClipRect(r, op, edgeStyle); 1002 } 1003 1004 virtual void onClipRRect(const SkRRect& rrect, 1005 SkRegion::Op op, 1006 ClipEdgeStyle edgeStyle)override { 1007 fClipCount += 1; 1008 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1009 } 1010 1011 virtual void onClipPath(const SkPath& path, 1012 SkRegion::Op op, 1013 ClipEdgeStyle edgeStyle) override { 1014 fClipCount += 1; 1015 this->INHERITED::onClipPath(path, op, edgeStyle); 1016 } 1017 1018 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override { 1019 fClipCount += 1; 1020 this->INHERITED::onClipRegion(deviceRgn, op); 1021 } 1022 1023 unsigned getClipCount() const { return fClipCount; } 1024 1025private: 1026 unsigned fClipCount; 1027 1028 typedef SkCanvas INHERITED; 1029}; 1030 1031static void test_clip_expansion(skiatest::Reporter* reporter) { 1032 SkPictureRecorder recorder; 1033 SkCanvas* canvas = recorder.beginRecording(10, 10); 1034 1035 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1036 // The following expanding clip should not be skipped. 1037 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1038 // Draw something so the optimizer doesn't just fold the world. 1039 SkPaint p; 1040 p.setColor(SK_ColorBLUE); 1041 canvas->drawPaint(p); 1042 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1043 1044 ClipCountingCanvas testCanvas(10, 10); 1045 picture->playback(&testCanvas); 1046 1047 // Both clips should be present on playback. 1048 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1049} 1050 1051static void test_hierarchical(skiatest::Reporter* reporter) { 1052 SkBitmap bm; 1053 make_bm(&bm, 10, 10, SK_ColorRED, true); 1054 1055 SkPictureRecorder recorder; 1056 1057 recorder.beginRecording(10, 10); 1058 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1059 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1060 1061 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1062 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1063 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1064 1065 { 1066 SkCanvas* canvas = recorder.beginRecording(10, 10); 1067 canvas->drawPicture(childPlain); 1068 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1069 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1070 } 1071 { 1072 SkCanvas* canvas = recorder.beginRecording(10, 10); 1073 canvas->drawPicture(childWithBitmap); 1074 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1075 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1076 } 1077 { 1078 SkCanvas* canvas = recorder.beginRecording(10, 10); 1079 canvas->drawBitmap(bm, 0, 0); 1080 canvas->drawPicture(childPlain); 1081 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1082 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1083 } 1084 { 1085 SkCanvas* canvas = recorder.beginRecording(10, 10); 1086 canvas->drawBitmap(bm, 0, 0); 1087 canvas->drawPicture(childWithBitmap); 1088 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1089 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1090 } 1091} 1092 1093static void test_gen_id(skiatest::Reporter* reporter) { 1094 1095 SkPictureRecorder recorder; 1096 recorder.beginRecording(0, 0); 1097 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1098 1099 // Empty pictures should still have a valid ID 1100 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 1101 1102 SkCanvas* canvas = recorder.beginRecording(1, 1); 1103 canvas->drawARGB(255, 255, 255, 255); 1104 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1105 // picture should have a non-zero id after recording 1106 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1107 1108 // both pictures should have different ids 1109 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 1110} 1111 1112DEF_TEST(Picture, reporter) { 1113#ifdef SK_DEBUG 1114 test_deleting_empty_picture(); 1115 test_serializing_empty_picture(); 1116#else 1117 test_bad_bitmap(); 1118#endif 1119 test_unbalanced_save_restores(reporter); 1120 test_peephole(); 1121#if SK_SUPPORT_GPU 1122 test_gpu_veto(reporter); 1123#endif 1124 test_has_text(reporter); 1125 test_analysis(reporter); 1126 test_bitmap_with_encoded_data(reporter); 1127 test_clip_bound_opt(reporter); 1128 test_clip_expansion(reporter); 1129 test_hierarchical(reporter); 1130 test_gen_id(reporter); 1131 test_savelayer_extraction(reporter); 1132} 1133 1134static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1135 const SkPaint paint; 1136 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1137 const SkIRect irect = { 2, 2, 3, 3 }; 1138 1139 // Don't care what these record, as long as they're legal. 1140 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1141 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1142 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1143 canvas->drawSprite(bitmap, 1, 1); 1144} 1145 1146static void test_draw_bitmaps(SkCanvas* canvas) { 1147 SkBitmap empty; 1148 draw_bitmaps(empty, canvas); 1149 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1150 draw_bitmaps(empty, canvas); 1151} 1152 1153DEF_TEST(Picture_EmptyBitmap, r) { 1154 SkPictureRecorder recorder; 1155 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1156 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1157} 1158 1159DEF_TEST(Canvas_EmptyBitmap, r) { 1160 SkBitmap dst; 1161 dst.allocN32Pixels(10, 10); 1162 SkCanvas canvas(dst); 1163 1164 test_draw_bitmaps(&canvas); 1165} 1166 1167DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1168 // This test is from crbug.com/344987. 1169 // The commands are: 1170 // saveLayer with paint that modifies alpha 1171 // drawBitmapRectToRect 1172 // drawBitmapRectToRect 1173 // restore 1174 // The bug was that this structure was modified so that: 1175 // - The saveLayer and restore were eliminated 1176 // - The alpha was only applied to the first drawBitmapRectToRect 1177 1178 // This test draws blue and red squares inside a 50% transparent 1179 // layer. Both colours should show up muted. 1180 // When the bug is present, the red square (the second bitmap) 1181 // shows upwith full opacity. 1182 1183 SkBitmap blueBM; 1184 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1185 SkBitmap redBM; 1186 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1187 SkPaint semiTransparent; 1188 semiTransparent.setAlpha(0x80); 1189 1190 SkPictureRecorder recorder; 1191 SkCanvas* canvas = recorder.beginRecording(100, 100); 1192 canvas->drawARGB(0, 0, 0, 0); 1193 1194 canvas->saveLayer(0, &semiTransparent); 1195 canvas->drawBitmap(blueBM, 25, 25); 1196 canvas->drawBitmap(redBM, 50, 50); 1197 canvas->restore(); 1198 1199 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1200 1201 // Now replay the picture back on another canvas 1202 // and check a couple of its pixels. 1203 SkBitmap replayBM; 1204 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1205 SkCanvas replayCanvas(replayBM); 1206 picture->playback(&replayCanvas); 1207 replayCanvas.flush(); 1208 1209 // With the bug present, at (55, 55) we would get a fully opaque red 1210 // intead of a dark red. 1211 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1212 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1213} 1214 1215struct CountingBBH : public SkBBoxHierarchy { 1216 mutable int searchCalls; 1217 SkRect rootBound; 1218 1219 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 1220 1221 void search(const SkRect& query, SkTDArray<unsigned>* results) const override { 1222 this->searchCalls++; 1223 } 1224 1225 void insert(const SkRect[], int) override {} 1226 virtual size_t bytesUsed() const override { return 0; } 1227 SkRect getRootBound() const override { return rootBound; } 1228}; 1229 1230class SpoonFedBBHFactory : public SkBBHFactory { 1231public: 1232 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1233 SkBBoxHierarchy* operator()(const SkRect&) const override { 1234 return SkRef(fBBH); 1235 } 1236private: 1237 SkBBoxHierarchy* fBBH; 1238}; 1239 1240// When the canvas clip covers the full picture, we don't need to call the BBH. 1241DEF_TEST(Picture_SkipBBH, r) { 1242 SkRect bound = SkRect::MakeWH(320, 240); 1243 CountingBBH bbh(bound); 1244 SpoonFedBBHFactory factory(&bbh); 1245 1246 SkPictureRecorder recorder; 1247 SkCanvas* c = recorder.beginRecording(bound, &factory); 1248 // Record a few ops so we don't hit a small- or empty- picture optimization. 1249 c->drawRect(bound, SkPaint()); 1250 c->drawRect(bound, SkPaint()); 1251 SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); 1252 1253 SkCanvas big(640, 480), small(300, 200); 1254 1255 picture->playback(&big); 1256 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1257 1258 picture->playback(&small); 1259 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1260} 1261 1262DEF_TEST(Picture_BitmapLeak, r) { 1263 SkBitmap mut, immut; 1264 mut.allocN32Pixels(300, 200); 1265 immut.allocN32Pixels(300, 200); 1266 immut.setImmutable(); 1267 SkASSERT(!mut.isImmutable()); 1268 SkASSERT(immut.isImmutable()); 1269 1270 // No one can hold a ref on our pixels yet. 1271 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1272 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1273 1274 SkAutoTUnref<const SkPicture> pic; 1275 { 1276 // we want the recorder to go out of scope before our subsequent checks, so we 1277 // place it inside local braces. 1278 SkPictureRecorder rec; 1279 SkCanvas* canvas = rec.beginRecording(1920, 1200); 1280 canvas->drawBitmap(mut, 0, 0); 1281 canvas->drawBitmap(immut, 800, 600); 1282 pic.reset(rec.endRecording()); 1283 } 1284 1285 // The picture shares the immutable pixels but copies the mutable ones. 1286 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1287 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 1288 1289 // When the picture goes away, it's just our bitmaps holding the refs. 1290 pic.reset(NULL); 1291 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1292 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1293} 1294 1295// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 1296DEF_TEST(Picture_getRecordingCanvas, r) { 1297 SkPictureRecorder rec; 1298 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1299 for (int i = 0; i < 3; i++) { 1300 rec.beginRecording(100, 100); 1301 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 1302 rec.endRecording()->unref(); 1303 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1304 } 1305} 1306 1307DEF_TEST(MiniRecorderLeftHanging, r) { 1308 // Any shader or other ref-counted effect will do just fine here. 1309 SkPaint paint; 1310 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref(); 1311 1312 SkMiniRecorder rec; 1313 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 1314 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 1315} 1316