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