PictureTest.cpp revision f5822825ec2d5dd24ab476fdd42db2a6573f9756
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, shader->isABitmap()); 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 const SkBigPicture* bp = pict->asSkBigPicture(); 367 REPORTER_ASSERT(reporter, bp); 368 369 const SkBigPicture::AccelData* data = bp->accelData(); 370 REPORTER_ASSERT(reporter, data); 371 372 const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data); 373 REPORTER_ASSERT(reporter, 8 == gpuData->numBlocks()); 374 375 const SkLayerInfo::BlockInfo& info0 = gpuData->block(0); 376 // The parent/child layers appear in reverse order 377 const SkLayerInfo::BlockInfo& info1 = gpuData->block(2); 378 const SkLayerInfo::BlockInfo& info2 = gpuData->block(1); 379 380 const SkLayerInfo::BlockInfo& info3 = gpuData->block(3); 381 382 // The parent/child layers appear in reverse order 383 const SkLayerInfo::BlockInfo& info4 = gpuData->block(5); 384 const SkLayerInfo::BlockInfo& info5 = gpuData->block(4); 385 386 // The parent/child layers appear in reverse order 387 const SkLayerInfo::BlockInfo& info6 = gpuData->block(7); 388 const SkLayerInfo::BlockInfo& info7 = gpuData->block(6); 389 390 REPORTER_ASSERT(reporter, NULL == info0.fPicture); 391 REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() && 392 kHeight == info0.fBounds.height()); 393 REPORTER_ASSERT(reporter, info0.fLocalMat.isIdentity()); 394 REPORTER_ASSERT(reporter, info0.fPreMat.isIdentity()); 395 REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop); 396 REPORTER_ASSERT(reporter, NULL != info0.fPaint); 397 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); 398 399 REPORTER_ASSERT(reporter, NULL == info1.fPicture); 400 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.width() && 401 kHeight/2.0 == info1.fBounds.height()); 402 REPORTER_ASSERT(reporter, info1.fLocalMat.isIdentity()); 403 REPORTER_ASSERT(reporter, info1.fPreMat.isIdentity()); 404 REPORTER_ASSERT(reporter, kWidth/2.0 == info1.fBounds.fLeft && 405 kHeight/2.0 == info1.fBounds.fTop); 406 REPORTER_ASSERT(reporter, NULL == info1.fPaint); 407 REPORTER_ASSERT(reporter, !info1.fIsNested && 408 info1.fHasNestedLayers); // has a nested SL 409 410 REPORTER_ASSERT(reporter, NULL == info2.fPicture); 411 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() && 412 kHeight / 2 == info2.fBounds.height()); // bound reduces size 413 REPORTER_ASSERT(reporter, !info2.fLocalMat.isIdentity()); 414 REPORTER_ASSERT(reporter, info2.fPreMat.isIdentity()); 415 REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated 416 kHeight / 2 == info2.fBounds.fTop); 417 REPORTER_ASSERT(reporter, NULL != info2.fPaint); 418 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested 419 420 REPORTER_ASSERT(reporter, NULL == info3.fPicture); 421 REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() && 422 kHeight == info3.fBounds.height()); 423 REPORTER_ASSERT(reporter, info3.fLocalMat.isIdentity()); 424 REPORTER_ASSERT(reporter, info3.fPreMat.isIdentity()); 425 REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop); 426 REPORTER_ASSERT(reporter, info3.fPaint); 427 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); 428 429 REPORTER_ASSERT(reporter, NULL == info4.fPicture); 430 REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() && 431 kHeight == info4.fBounds.height()); 432 REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop); 433 REPORTER_ASSERT(reporter, info4.fLocalMat.isIdentity()); 434 REPORTER_ASSERT(reporter, info4.fPreMat.isIdentity()); 435 REPORTER_ASSERT(reporter, info4.fPaint); 436 REPORTER_ASSERT(reporter, !info4.fIsNested && 437 info4.fHasNestedLayers); // has a nested SL 438 439 REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture 440 REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() && 441 kHeight == info5.fBounds.height()); 442 REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop); 443 REPORTER_ASSERT(reporter, info5.fLocalMat.isIdentity()); 444 REPORTER_ASSERT(reporter, info5.fPreMat.isIdentity()); 445 REPORTER_ASSERT(reporter, NULL != info5.fPaint); 446 REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested 447 448 REPORTER_ASSERT(reporter, NULL == info6.fPicture); 449 REPORTER_ASSERT(reporter, kWidth-10 == info6.fBounds.width() && 450 kHeight-10 == info6.fBounds.height()); 451 REPORTER_ASSERT(reporter, 10 == info6.fBounds.fLeft && 10 == info6.fBounds.fTop); 452 REPORTER_ASSERT(reporter, info6.fLocalMat.isIdentity()); 453 REPORTER_ASSERT(reporter, info6.fPreMat.isIdentity()); 454 REPORTER_ASSERT(reporter, info6.fPaint); 455 REPORTER_ASSERT(reporter, !info6.fIsNested && 456 info6.fHasNestedLayers); // has a nested SL 457 458 REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture 459 REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() && 460 kHeight == info7.fBounds.height()); 461 REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop); 462 REPORTER_ASSERT(reporter, info7.fLocalMat.isIdentity()); 463 REPORTER_ASSERT(reporter, info7.fPreMat.isIdentity()); 464 REPORTER_ASSERT(reporter, NULL != info7.fPaint); 465 REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested 466 } 467} 468 469static void test_has_text(skiatest::Reporter* reporter) { 470 SkPictureRecorder recorder; 471 472 SkCanvas* canvas = recorder.beginRecording(100,100); 473 { 474 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint()); 475 } 476 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 477 REPORTER_ASSERT(reporter, !picture->hasText()); 478 479 SkPoint point = SkPoint::Make(10, 10); 480 canvas = recorder.beginRecording(100,100); 481 { 482 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint()); 483 } 484 picture.reset(recorder.endRecording()); 485 REPORTER_ASSERT(reporter, picture->hasText()); 486 487 canvas = recorder.beginRecording(100,100); 488 { 489 canvas->drawPosText("Q", 1, &point, SkPaint()); 490 } 491 picture.reset(recorder.endRecording()); 492 REPORTER_ASSERT(reporter, picture->hasText()); 493 494 canvas = recorder.beginRecording(100,100); 495 { 496 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint()); 497 } 498 picture.reset(recorder.endRecording()); 499 REPORTER_ASSERT(reporter, picture->hasText()); 500 501 canvas = recorder.beginRecording(100,100); 502 { 503 SkPath path; 504 path.moveTo(0, 0); 505 path.lineTo(50, 50); 506 507 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint()); 508 } 509 picture.reset(recorder.endRecording()); 510 REPORTER_ASSERT(reporter, picture->hasText()); 511 512 canvas = recorder.beginRecording(100,100); 513 { 514 SkPath path; 515 path.moveTo(0, 0); 516 path.lineTo(50, 50); 517 518 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint()); 519 } 520 picture.reset(recorder.endRecording()); 521 REPORTER_ASSERT(reporter, picture->hasText()); 522 523 // Nest the previous picture inside a new one. 524 canvas = recorder.beginRecording(100,100); 525 { 526 canvas->drawPicture(picture.get()); 527 } 528 picture.reset(recorder.endRecording()); 529 REPORTER_ASSERT(reporter, picture->hasText()); 530} 531 532static void set_canvas_to_save_count_4(SkCanvas* canvas) { 533 canvas->restoreToCount(1); 534 canvas->save(); 535 canvas->save(); 536 canvas->save(); 537} 538 539/** 540 * A canvas that records the number of saves, saveLayers and restores. 541 */ 542class SaveCountingCanvas : public SkCanvas { 543public: 544 SaveCountingCanvas(int width, int height) 545 : INHERITED(width, height) 546 , fSaveCount(0) 547 , fSaveLayerCount(0) 548 , fRestoreCount(0){ 549 } 550 551 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, 552 SaveFlags flags) override { 553 ++fSaveLayerCount; 554 return this->INHERITED::willSaveLayer(bounds, paint, flags); 555 } 556 557 void willSave() override { 558 ++fSaveCount; 559 this->INHERITED::willSave(); 560 } 561 562 void willRestore() override { 563 ++fRestoreCount; 564 this->INHERITED::willRestore(); 565 } 566 567 unsigned int getSaveCount() const { return fSaveCount; } 568 unsigned int getSaveLayerCount() const { return fSaveLayerCount; } 569 unsigned int getRestoreCount() const { return fRestoreCount; } 570 571private: 572 unsigned int fSaveCount; 573 unsigned int fSaveLayerCount; 574 unsigned int fRestoreCount; 575 576 typedef SkCanvas INHERITED; 577}; 578 579void check_save_state(skiatest::Reporter* reporter, SkPicture* picture, 580 unsigned int numSaves, unsigned int numSaveLayers, 581 unsigned int numRestores) { 582 SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()), 583 SkScalarCeilToInt(picture->cullRect().height())); 584 585 picture->playback(&canvas); 586 587 // Optimizations may have removed these, 588 // so expect to have seen no more than num{Saves,SaveLayers,Restores}. 589 REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount()); 590 REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount()); 591 REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount()); 592} 593 594// This class exists so SkPicture can friend it and give it access to 595// the 'partialReplay' method. 596class SkPictureRecorderReplayTester { 597public: 598 static SkPicture* Copy(SkPictureRecorder* recorder) { 599 SkPictureRecorder recorder2; 600 601 SkCanvas* canvas = recorder2.beginRecording(10, 10); 602 603 recorder->partialReplay(canvas); 604 605 return recorder2.endRecording(); 606 } 607}; 608 609static void create_imbalance(SkCanvas* canvas) { 610 SkRect clipRect = SkRect::MakeWH(2, 2); 611 SkRect drawRect = SkRect::MakeWH(10, 10); 612 canvas->save(); 613 canvas->clipRect(clipRect, SkRegion::kReplace_Op); 614 canvas->translate(1.0f, 1.0f); 615 SkPaint p; 616 p.setColor(SK_ColorGREEN); 617 canvas->drawRect(drawRect, p); 618 // no restore 619} 620 621// This tests that replaying a potentially unbalanced picture into a canvas 622// doesn't affect the canvas' save count or matrix/clip state. 623static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) { 624 SkBitmap bm; 625 bm.allocN32Pixels(4, 3); 626 SkCanvas canvas(bm); 627 628 int beforeSaveCount = canvas.getSaveCount(); 629 630 SkMatrix beforeMatrix = canvas.getTotalMatrix(); 631 632 SkRect beforeClip; 633 634 canvas.getClipBounds(&beforeClip); 635 636 canvas.drawPicture(picture); 637 638 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 639 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 640 641 SkRect afterClip; 642 643 canvas.getClipBounds(&afterClip); 644 645 REPORTER_ASSERT(reporter, afterClip == beforeClip); 646} 647 648// Test out SkPictureRecorder::partialReplay 649DEF_TEST(PictureRecorder_replay, reporter) { 650 // check save/saveLayer state 651 { 652 SkPictureRecorder recorder; 653 654 SkCanvas* canvas = recorder.beginRecording(10, 10); 655 656 canvas->saveLayer(NULL, NULL); 657 658 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 659 660 // The extra save and restore comes from the Copy process. 661 check_save_state(reporter, copy, 2, 1, 3); 662 663 canvas->saveLayer(NULL, NULL); 664 665 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 666 667 check_save_state(reporter, final, 1, 2, 3); 668 669 // The copy shouldn't pick up any operations added after it was made 670 check_save_state(reporter, copy, 2, 1, 3); 671 } 672 673 // (partially) check leakage of draw ops 674 { 675 SkPictureRecorder recorder; 676 677 SkCanvas* canvas = recorder.beginRecording(10, 10); 678 679 SkRect r = SkRect::MakeWH(5, 5); 680 SkPaint p; 681 682 canvas->drawRect(r, p); 683 684 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 685 686 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 687 688 SkBitmap bm; 689 make_bm(&bm, 10, 10, SK_ColorRED, true); 690 691 r.offset(5.0f, 5.0f); 692 canvas->drawBitmapRect(bm, r, nullptr); 693 694 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 695 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); 696 697 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); 698 699 // The snapshot shouldn't pick up any operations added after it was made 700 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 701 } 702 703 // Recreate the Android partialReplay test case 704 { 705 SkPictureRecorder recorder; 706 707 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0); 708 create_imbalance(canvas); 709 710 int expectedSaveCount = canvas->getSaveCount(); 711 712 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 713 check_balance(reporter, copy); 714 715 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 716 717 // End the recording of source to test the picture finalization 718 // process isn't complicated by the partialReplay step 719 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 720 } 721} 722 723static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 724 SkCanvas testCanvas(100, 100); 725 set_canvas_to_save_count_4(&testCanvas); 726 727 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 728 729 SkPaint paint; 730 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 731 732 SkPictureRecorder recorder; 733 734 { 735 // Create picture with 2 unbalanced saves 736 SkCanvas* canvas = recorder.beginRecording(100, 100); 737 canvas->save(); 738 canvas->translate(10, 10); 739 canvas->drawRect(rect, paint); 740 canvas->save(); 741 canvas->translate(10, 10); 742 canvas->drawRect(rect, paint); 743 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording()); 744 745 testCanvas.drawPicture(extraSavePicture); 746 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 747 } 748 749 set_canvas_to_save_count_4(&testCanvas); 750 751 { 752 // Create picture with 2 unbalanced restores 753 SkCanvas* canvas = recorder.beginRecording(100, 100); 754 canvas->save(); 755 canvas->translate(10, 10); 756 canvas->drawRect(rect, paint); 757 canvas->save(); 758 canvas->translate(10, 10); 759 canvas->drawRect(rect, paint); 760 canvas->restore(); 761 canvas->restore(); 762 canvas->restore(); 763 canvas->restore(); 764 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording()); 765 766 testCanvas.drawPicture(extraRestorePicture); 767 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 768 } 769 770 set_canvas_to_save_count_4(&testCanvas); 771 772 { 773 SkCanvas* canvas = recorder.beginRecording(100, 100); 774 canvas->translate(10, 10); 775 canvas->drawRect(rect, paint); 776 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording()); 777 778 testCanvas.drawPicture(noSavePicture); 779 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 780 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 781 } 782} 783 784static void test_peephole() { 785 SkRandom rand; 786 787 SkPictureRecorder recorder; 788 789 for (int j = 0; j < 100; j++) { 790 SkRandom rand2(rand); // remember the seed 791 792 SkCanvas* canvas = recorder.beginRecording(100, 100); 793 794 for (int i = 0; i < 1000; ++i) { 795 rand_op(canvas, rand); 796 } 797 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 798 799 rand = rand2; 800 } 801 802 { 803 SkCanvas* canvas = recorder.beginRecording(100, 100); 804 SkRect rect = SkRect::MakeWH(50, 50); 805 806 for (int i = 0; i < 100; ++i) { 807 canvas->save(); 808 } 809 while (canvas->getSaveCount() > 1) { 810 canvas->clipRect(rect); 811 canvas->restore(); 812 } 813 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 814 } 815} 816 817#ifndef SK_DEBUG 818// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 819// should never do this. 820static void test_bad_bitmap() { 821 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 822 // fail. 823 SkBitmap bm; 824 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 825 SkPictureRecorder recorder; 826 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 827 recordingCanvas->drawBitmap(bm, 0, 0); 828 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 829 830 SkCanvas canvas; 831 canvas.drawPicture(picture); 832} 833#endif 834 835static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 836 SkPictureRecorder recorder; 837 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()), 838 SkIntToScalar(bitmap.height())); 839 canvas->drawBitmap(bitmap, 0, 0); 840 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 841 842 SkDynamicMemoryWStream wStream; 843 sk_tool_utils::PngPixelSerializer serializer; 844 picture->serialize(&wStream, &serializer); 845 return wStream.copyToData(); 846} 847 848struct ErrorContext { 849 int fErrors; 850 skiatest::Reporter* fReporter; 851}; 852 853static void assert_one_parse_error_cb(SkError error, void* context) { 854 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 855 errorContext->fErrors++; 856 // This test only expects one error, and that is a kParseError. If there are others, 857 // there is some unknown problem. 858 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 859 "This threw more errors than expected."); 860 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 861 SkGetLastErrorString()); 862} 863 864static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 865 // Create a bitmap that will be encoded. 866 SkBitmap original; 867 make_bm(&original, 100, 100, SK_ColorBLUE, true); 868 SkDynamicMemoryWStream wStream; 869 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 870 return; 871 } 872 SkAutoDataUnref data(wStream.copyToData()); 873 874 SkBitmap bm; 875 bool installSuccess = SkInstallDiscardablePixelRef(data, &bm); 876 REPORTER_ASSERT(reporter, installSuccess); 877 878 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 879 // Flattening original will follow the old path of performing an encode, while flattening bm 880 // will use the already encoded data. 881 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 882 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 883 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 884 // Now test that a parse error was generated when trying to create a new SkPicture without 885 // providing a function to decode the bitmap. 886 ErrorContext context; 887 context.fErrors = 0; 888 context.fReporter = reporter; 889 SkSetErrorCallback(assert_one_parse_error_cb, &context); 890 SkMemoryStream pictureStream(picture1); 891 SkClearLastError(); 892 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 893 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 894 SkClearLastError(); 895 SkSetErrorCallback(NULL, NULL); 896} 897 898static void test_clip_bound_opt(skiatest::Reporter* reporter) { 899 // Test for crbug.com/229011 900 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 901 SkIntToScalar(2), SkIntToScalar(2)); 902 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 903 SkIntToScalar(1), SkIntToScalar(1)); 904 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 905 SkIntToScalar(1), SkIntToScalar(1)); 906 907 SkPath invPath; 908 invPath.addOval(rect1); 909 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 910 SkPath path; 911 path.addOval(rect2); 912 SkPath path2; 913 path2.addOval(rect3); 914 SkIRect clipBounds; 915 SkPictureRecorder recorder; 916 917 // Testing conservative-raster-clip that is enabled by PictureRecord 918 { 919 SkCanvas* canvas = recorder.beginRecording(10, 10); 920 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 921 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 922 REPORTER_ASSERT(reporter, true == nonEmpty); 923 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 924 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 925 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 926 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 927 } 928 { 929 SkCanvas* canvas = recorder.beginRecording(10, 10); 930 canvas->clipPath(path, SkRegion::kIntersect_Op); 931 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 932 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 933 REPORTER_ASSERT(reporter, true == nonEmpty); 934 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 935 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 936 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 937 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 938 } 939 { 940 SkCanvas* canvas = recorder.beginRecording(10, 10); 941 canvas->clipPath(path, SkRegion::kIntersect_Op); 942 canvas->clipPath(invPath, SkRegion::kUnion_Op); 943 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 944 REPORTER_ASSERT(reporter, true == nonEmpty); 945 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 946 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 947 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 948 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 949 } 950 { 951 SkCanvas* canvas = recorder.beginRecording(10, 10); 952 canvas->clipPath(path, SkRegion::kDifference_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::kReverseDifference_Op); 963 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 964 // True clip is actually empty in this case, but the best 965 // determination we can make using only bounds as input is that the 966 // clip is included in the bounds of 'path'. 967 REPORTER_ASSERT(reporter, true == nonEmpty); 968 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 969 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 970 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 971 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 972 } 973 { 974 SkCanvas* canvas = recorder.beginRecording(10, 10); 975 canvas->clipPath(path, SkRegion::kIntersect_Op); 976 canvas->clipPath(path2, SkRegion::kXOR_Op); 977 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 978 REPORTER_ASSERT(reporter, true == nonEmpty); 979 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 980 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 981 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 982 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 983 } 984} 985 986static void test_cull_rect_reset(skiatest::Reporter* reporter) { 987 SkPictureRecorder recorder; 988 SkRect bounds = SkRect::MakeWH(10, 10); 989 SkRTreeFactory factory; 990 SkCanvas* canvas = recorder.beginRecording(bounds, &factory); 991 bounds = SkRect::MakeWH(100, 100); 992 SkPaint paint; 993 canvas->drawRect(bounds, paint); 994 canvas->drawRect(bounds, paint); 995 SkAutoTUnref<const SkPicture> p(recorder.endRecordingAsPicture(bounds)); 996 const SkBigPicture* picture = p->asSkBigPicture(); 997 REPORTER_ASSERT(reporter, picture); 998 999 SkRect finalCullRect = picture->cullRect(); 1000 REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft); 1001 REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop); 1002 REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom); 1003 REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight); 1004 1005 const SkBBoxHierarchy* pictureBBH = picture->bbh(); 1006 SkRect bbhCullRect = pictureBBH->getRootBound(); 1007 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft); 1008 REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop); 1009 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom); 1010 REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight); 1011} 1012 1013 1014/** 1015 * A canvas that records the number of clip commands. 1016 */ 1017class ClipCountingCanvas : public SkCanvas { 1018public: 1019 ClipCountingCanvas(int width, int height) 1020 : INHERITED(width, height) 1021 , fClipCount(0){ 1022 } 1023 1024 virtual void onClipRect(const SkRect& r, 1025 SkRegion::Op op, 1026 ClipEdgeStyle edgeStyle) override { 1027 fClipCount += 1; 1028 this->INHERITED::onClipRect(r, op, edgeStyle); 1029 } 1030 1031 virtual void onClipRRect(const SkRRect& rrect, 1032 SkRegion::Op op, 1033 ClipEdgeStyle edgeStyle)override { 1034 fClipCount += 1; 1035 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1036 } 1037 1038 virtual void onClipPath(const SkPath& path, 1039 SkRegion::Op op, 1040 ClipEdgeStyle edgeStyle) override { 1041 fClipCount += 1; 1042 this->INHERITED::onClipPath(path, op, edgeStyle); 1043 } 1044 1045 void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override { 1046 fClipCount += 1; 1047 this->INHERITED::onClipRegion(deviceRgn, op); 1048 } 1049 1050 unsigned getClipCount() const { return fClipCount; } 1051 1052private: 1053 unsigned fClipCount; 1054 1055 typedef SkCanvas INHERITED; 1056}; 1057 1058static void test_clip_expansion(skiatest::Reporter* reporter) { 1059 SkPictureRecorder recorder; 1060 SkCanvas* canvas = recorder.beginRecording(10, 10); 1061 1062 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1063 // The following expanding clip should not be skipped. 1064 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1065 // Draw something so the optimizer doesn't just fold the world. 1066 SkPaint p; 1067 p.setColor(SK_ColorBLUE); 1068 canvas->drawPaint(p); 1069 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1070 1071 ClipCountingCanvas testCanvas(10, 10); 1072 picture->playback(&testCanvas); 1073 1074 // Both clips should be present on playback. 1075 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1076} 1077 1078static void test_hierarchical(skiatest::Reporter* reporter) { 1079 SkBitmap bm; 1080 make_bm(&bm, 10, 10, SK_ColorRED, true); 1081 1082 SkPictureRecorder recorder; 1083 1084 recorder.beginRecording(10, 10); 1085 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1086 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1087 1088 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1089 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1090 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1091 1092 { 1093 SkCanvas* canvas = recorder.beginRecording(10, 10); 1094 canvas->drawPicture(childPlain); 1095 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1096 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1097 } 1098 { 1099 SkCanvas* canvas = recorder.beginRecording(10, 10); 1100 canvas->drawPicture(childWithBitmap); 1101 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1102 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1103 } 1104 { 1105 SkCanvas* canvas = recorder.beginRecording(10, 10); 1106 canvas->drawBitmap(bm, 0, 0); 1107 canvas->drawPicture(childPlain); 1108 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1109 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1110 } 1111 { 1112 SkCanvas* canvas = recorder.beginRecording(10, 10); 1113 canvas->drawBitmap(bm, 0, 0); 1114 canvas->drawPicture(childWithBitmap); 1115 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1116 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1117 } 1118} 1119 1120static void test_gen_id(skiatest::Reporter* reporter) { 1121 1122 SkPictureRecorder recorder; 1123 recorder.beginRecording(0, 0); 1124 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1125 1126 // Empty pictures should still have a valid ID 1127 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 1128 1129 SkCanvas* canvas = recorder.beginRecording(1, 1); 1130 canvas->drawARGB(255, 255, 255, 255); 1131 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1132 // picture should have a non-zero id after recording 1133 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1134 1135 // both pictures should have different ids 1136 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 1137} 1138 1139DEF_TEST(Picture, reporter) { 1140#ifdef SK_DEBUG 1141 test_deleting_empty_picture(); 1142 test_serializing_empty_picture(); 1143#else 1144 test_bad_bitmap(); 1145#endif 1146 test_unbalanced_save_restores(reporter); 1147 test_peephole(); 1148#if SK_SUPPORT_GPU 1149 test_gpu_veto(reporter); 1150#endif 1151 test_has_text(reporter); 1152 test_analysis(reporter); 1153 test_bitmap_with_encoded_data(reporter); 1154 test_clip_bound_opt(reporter); 1155 test_clip_expansion(reporter); 1156 test_hierarchical(reporter); 1157 test_gen_id(reporter); 1158 test_savelayer_extraction(reporter); 1159 test_cull_rect_reset(reporter); 1160} 1161 1162static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1163 const SkPaint paint; 1164 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1165 const SkIRect irect = { 2, 2, 3, 3 }; 1166 1167 // Don't care what these record, as long as they're legal. 1168 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1169 canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); 1170 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1171 canvas->drawSprite(bitmap, 1, 1); 1172} 1173 1174static void test_draw_bitmaps(SkCanvas* canvas) { 1175 SkBitmap empty; 1176 draw_bitmaps(empty, canvas); 1177 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1178 draw_bitmaps(empty, canvas); 1179} 1180 1181DEF_TEST(Picture_EmptyBitmap, r) { 1182 SkPictureRecorder recorder; 1183 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1184 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1185} 1186 1187DEF_TEST(Canvas_EmptyBitmap, r) { 1188 SkBitmap dst; 1189 dst.allocN32Pixels(10, 10); 1190 SkCanvas canvas(dst); 1191 1192 test_draw_bitmaps(&canvas); 1193} 1194 1195DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1196 // This test is from crbug.com/344987. 1197 // The commands are: 1198 // saveLayer with paint that modifies alpha 1199 // drawBitmapRect 1200 // drawBitmapRect 1201 // restore 1202 // The bug was that this structure was modified so that: 1203 // - The saveLayer and restore were eliminated 1204 // - The alpha was only applied to the first drawBitmapRectToRect 1205 1206 // This test draws blue and red squares inside a 50% transparent 1207 // layer. Both colours should show up muted. 1208 // When the bug is present, the red square (the second bitmap) 1209 // shows upwith full opacity. 1210 1211 SkBitmap blueBM; 1212 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1213 SkBitmap redBM; 1214 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1215 SkPaint semiTransparent; 1216 semiTransparent.setAlpha(0x80); 1217 1218 SkPictureRecorder recorder; 1219 SkCanvas* canvas = recorder.beginRecording(100, 100); 1220 canvas->drawARGB(0, 0, 0, 0); 1221 1222 canvas->saveLayer(0, &semiTransparent); 1223 canvas->drawBitmap(blueBM, 25, 25); 1224 canvas->drawBitmap(redBM, 50, 50); 1225 canvas->restore(); 1226 1227 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1228 1229 // Now replay the picture back on another canvas 1230 // and check a couple of its pixels. 1231 SkBitmap replayBM; 1232 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1233 SkCanvas replayCanvas(replayBM); 1234 picture->playback(&replayCanvas); 1235 replayCanvas.flush(); 1236 1237 // With the bug present, at (55, 55) we would get a fully opaque red 1238 // intead of a dark red. 1239 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1240 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1241} 1242 1243struct CountingBBH : public SkBBoxHierarchy { 1244 mutable int searchCalls; 1245 SkRect rootBound; 1246 1247 CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} 1248 1249 void search(const SkRect& query, SkTDArray<int>* results) const override { 1250 this->searchCalls++; 1251 } 1252 1253 void insert(const SkRect[], int) override {} 1254 virtual size_t bytesUsed() const override { return 0; } 1255 SkRect getRootBound() const override { return rootBound; } 1256}; 1257 1258class SpoonFedBBHFactory : public SkBBHFactory { 1259public: 1260 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1261 SkBBoxHierarchy* operator()(const SkRect&) const override { 1262 return SkRef(fBBH); 1263 } 1264private: 1265 SkBBoxHierarchy* fBBH; 1266}; 1267 1268// When the canvas clip covers the full picture, we don't need to call the BBH. 1269DEF_TEST(Picture_SkipBBH, r) { 1270 SkRect bound = SkRect::MakeWH(320, 240); 1271 CountingBBH bbh(bound); 1272 SpoonFedBBHFactory factory(&bbh); 1273 1274 SkPictureRecorder recorder; 1275 SkCanvas* c = recorder.beginRecording(bound, &factory); 1276 // Record a few ops so we don't hit a small- or empty- picture optimization. 1277 c->drawRect(bound, SkPaint()); 1278 c->drawRect(bound, SkPaint()); 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 1323// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. 1324DEF_TEST(Picture_getRecordingCanvas, r) { 1325 SkPictureRecorder rec; 1326 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1327 for (int i = 0; i < 3; i++) { 1328 rec.beginRecording(100, 100); 1329 REPORTER_ASSERT(r, rec.getRecordingCanvas()); 1330 rec.endRecording()->unref(); 1331 REPORTER_ASSERT(r, !rec.getRecordingCanvas()); 1332 } 1333} 1334 1335DEF_TEST(MiniRecorderLeftHanging, r) { 1336 // Any shader or other ref-counted effect will do just fine here. 1337 SkPaint paint; 1338 paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref(); 1339 1340 SkMiniRecorder rec; 1341 REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); 1342 // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. 1343} 1344 1345DEF_TEST(Picture_preserveCullRect, r) { 1346 SkPictureRecorder recorder; 1347 1348 SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); 1349 c->clear(SK_ColorCYAN); 1350 1351 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1352 SkDynamicMemoryWStream wstream; 1353 picture->serialize(&wstream); 1354 1355 SkAutoTDelete<SkStream> rstream(wstream.detachAsStream()); 1356 SkAutoTUnref<SkPicture> deserializedPicture(SkPicture::CreateFromStream(rstream)); 1357 1358 REPORTER_ASSERT(r, SkToBool(deserializedPicture)); 1359 REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); 1360 REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); 1361 REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); 1362 REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); 1363} 1364