PictureTest.cpp revision 3d50ea1b87132833d7eab38964f40315ba553205
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 "Test.h" 9#include "TestClassDef.h" 10#include "SkBitmapDevice.h" 11#include "SkCanvas.h" 12#include "SkColorPriv.h" 13#include "SkData.h" 14#include "SkDecodingImageGenerator.h" 15#include "SkError.h" 16#include "SkImageEncoder.h" 17#include "SkImageGenerator.h" 18#include "SkPaint.h" 19#include "SkPicture.h" 20#include "SkPictureUtils.h" 21#include "SkRandom.h" 22#include "SkRRect.h" 23#include "SkShader.h" 24#include "SkStream.h" 25 26 27static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 28 bm->setConfig(SkBitmap::kARGB_8888_Config, w, h); 29 bm->allocPixels(); 30 bm->eraseColor(color); 31 if (immutable) { 32 bm->setImmutable(); 33 } 34} 35 36typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&); 37 38static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 39 const SkPoint& pos) { 40 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 41} 42 43static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 44 const SkPoint& pos) { 45 SkRect r = { 46 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 47 }; 48 r.offset(pos.fX, pos.fY); 49 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 50} 51 52static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm, 53 const SkPoint& pos) { 54 SkRect r = { 55 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) 56 }; 57 r.offset(pos.fX, pos.fY); 58 59 SkShader* s = SkShader::CreateBitmapShader(bm, 60 SkShader::kClamp_TileMode, 61 SkShader::kClamp_TileMode); 62 SkPaint paint; 63 paint.setShader(s)->unref(); 64 canvas->drawRect(r, paint); 65 canvas->drawOval(r, paint); 66 SkRRect rr; 67 rr.setRectXY(r, 10, 10); 68 canvas->drawRRect(rr, paint); 69} 70 71// Return a picture with the bitmaps drawn at the specified positions. 72static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[], 73 int count, DrawBitmapProc proc) { 74 SkPicture* pic = new SkPicture; 75 SkCanvas* canvas = pic->beginRecording(1000, 1000); 76 for (int i = 0; i < count; ++i) { 77 proc(canvas, bm[i], pos[i]); 78 } 79 pic->endRecording(); 80 return pic; 81} 82 83static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { 84 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 85 rect->fTop = rand.nextRangeScalar(-H, 2*H); 86 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 87 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 88 89 // we integralize rect to make our tests more predictable, since Gather is 90 // a little sloppy. 91 SkIRect ir; 92 rect->round(&ir); 93 rect->set(ir); 94} 95 96// Allocate result to be large enough to hold subset, and then draw the picture 97// into it, offsetting by subset's top/left corner. 98static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) { 99 SkIRect ir; 100 subset.roundOut(&ir); 101 int w = ir.width(); 102 int h = ir.height(); 103 make_bm(result, w, h, 0, false); 104 105 SkCanvas canvas(*result); 106 canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top())); 107 canvas.drawPicture(*pic); 108} 109 110template <typename T> int find_index(const T* array, T elem, int count) { 111 for (int i = 0; i < count; ++i) { 112 if (array[i] == elem) { 113 return i; 114 } 115 } 116 return -1; 117} 118 119// Return true if 'ref' is found in array[] 120static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 121 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 122} 123 124// Look at each pixel in bm, and if its color appears in colors[], find the 125// corresponding value in refs[] and append that ref into array, skipping 126// duplicates of the same value. 127static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[], 128 int count, SkTDArray<SkPixelRef*>* array) { 129 // Since we only want to return unique values in array, when we scan we just 130 // set a bit for each index'd color found. In practice we only have a few 131 // distinct colors, so we just use an int's bits as our array. Hence the 132 // assert that count <= number-of-bits-in-our-int. 133 SkASSERT((unsigned)count <= 32); 134 uint32_t bitarray = 0; 135 136 SkAutoLockPixels alp(bm); 137 138 for (int y = 0; y < bm.height(); ++y) { 139 for (int x = 0; x < bm.width(); ++x) { 140 SkPMColor pmc = *bm.getAddr32(x, y); 141 // the only good case where the color is not found would be if 142 // the color is transparent, meaning no bitmap was drawn in that 143 // pixel. 144 if (pmc) { 145 uint32_t index = SkGetPackedR32(pmc); 146 SkASSERT(SkGetPackedG32(pmc) == index); 147 SkASSERT(SkGetPackedB32(pmc) == index); 148 SkASSERT(static_cast<int>(index) < count); 149 bitarray |= 1 << index; 150 } 151 } 152 } 153 154 for (int i = 0; i < count; ++i) { 155 if (bitarray & (1 << i)) { 156 *array->append() = refs[i]; 157 } 158 } 159} 160 161static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 162 const int IW = 8; 163 const int IH = IW; 164 const SkScalar W = SkIntToScalar(IW); 165 const SkScalar H = W; 166 167 static const int N = 4; 168 SkBitmap bm[N]; 169 SkPixelRef* refs[N]; 170 171 const SkPoint pos[] = { 172 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 173 }; 174 175 // Our convention is that the color components contain the index of their 176 // corresponding bitmap/pixelref 177 for (int i = 0; i < N; ++i) { 178 make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true); 179 refs[i] = bm[i].pixelRef(); 180 } 181 182 static const DrawBitmapProc procs[] = { 183 drawbitmap_proc, drawbitmaprect_proc, drawshader_proc 184 }; 185 186 SkRandom rand; 187 for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) { 188 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k])); 189 190 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 191 // quick check for a small piece of each quadrant, which should just 192 // contain 1 bitmap. 193 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 194 SkRect r; 195 r.set(2, 2, W - 2, H - 2); 196 r.offset(pos[i].fX, pos[i].fY); 197 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 198 REPORTER_ASSERT(reporter, data); 199 if (data) { 200 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); 201 REPORTER_ASSERT(reporter, 1 == count); 202 REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]); 203 } 204 } 205 206 // Test a bunch of random (mostly) rects, and compare the gather results 207 // with a deduced list of refs by looking at the colors drawn. 208 for (int j = 0; j < 100; ++j) { 209 SkRect r; 210 rand_rect(&r, rand, 2*W, 2*H); 211 212 SkBitmap result; 213 draw(pic, r, &result); 214 SkTDArray<SkPixelRef*> array; 215 216 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 217 size_t dataSize = data ? data->size() : 0; 218 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); 219 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 220 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 221 SkAutoDataUnref adu(data); 222 223 gather_from_colors(result, refs, N, &array); 224 225 /* 226 * GatherPixelRefs is conservative, so it can return more bitmaps 227 * that we actually can see (usually because of conservative bounds 228 * inflation for antialiasing). Thus our check here is only that 229 * Gather didn't miss any that we actually saw. Even that isn't 230 * a strict requirement on Gather, which is meant to be quick and 231 * only mostly-correct, but at the moment this test should work. 232 */ 233 for (int i = 0; i < array.count(); ++i) { 234 bool found = find(gatherRefs, array[i], gatherCount); 235 REPORTER_ASSERT(reporter, found); 236#if 0 237 // enable this block of code to debug failures, as it will rerun 238 // the case that failed. 239 if (!found) { 240 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 241 size_t dataSize = data ? data->size() : 0; 242 } 243#endif 244 } 245 } 246 } 247} 248 249#ifdef SK_DEBUG 250// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 251// run in debug mode. 252static void test_deleting_empty_playback() { 253 SkPicture picture; 254 // Creates an SkPictureRecord 255 picture.beginRecording(0, 0); 256 // Turns that into an SkPicturePlayback 257 picture.endRecording(); 258 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 259 picture.beginRecording(0, 0); 260} 261 262// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 263static void test_serializing_empty_picture() { 264 SkPicture picture; 265 picture.beginRecording(0, 0); 266 picture.endRecording(); 267 SkDynamicMemoryWStream stream; 268 picture.serialize(&stream); 269} 270#endif 271 272static void rand_op(SkCanvas* canvas, SkRandom& rand) { 273 SkPaint paint; 274 SkRect rect = SkRect::MakeWH(50, 50); 275 276 SkScalar unit = rand.nextUScalar1(); 277 if (unit <= 0.3) { 278// SkDebugf("save\n"); 279 canvas->save(); 280 } else if (unit <= 0.6) { 281// SkDebugf("restore\n"); 282 canvas->restore(); 283 } else if (unit <= 0.9) { 284// SkDebugf("clip\n"); 285 canvas->clipRect(rect); 286 } else { 287// SkDebugf("draw\n"); 288 canvas->drawPaint(paint); 289 } 290} 291 292static void test_peephole() { 293 SkRandom rand; 294 295 for (int j = 0; j < 100; j++) { 296 SkRandom rand2(rand); // remember the seed 297 298 SkPicture picture; 299 SkCanvas* canvas = picture.beginRecording(100, 100); 300 301 for (int i = 0; i < 1000; ++i) { 302 rand_op(canvas, rand); 303 } 304 picture.endRecording(); 305 306 rand = rand2; 307 } 308 309 { 310 SkPicture picture; 311 SkCanvas* canvas = picture.beginRecording(100, 100); 312 SkRect rect = SkRect::MakeWH(50, 50); 313 314 for (int i = 0; i < 100; ++i) { 315 canvas->save(); 316 } 317 while (canvas->getSaveCount() > 1) { 318 canvas->clipRect(rect); 319 canvas->restore(); 320 } 321 picture.endRecording(); 322 } 323} 324 325#ifndef SK_DEBUG 326// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 327// should never do this. 328static void test_bad_bitmap() { 329 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 330 // fail. 331 SkBitmap bm; 332 bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); 333 SkPicture picture; 334 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 335 recordingCanvas->drawBitmap(bm, 0, 0); 336 picture.endRecording(); 337 338 SkCanvas canvas; 339 canvas.drawPicture(picture); 340} 341#endif 342 343static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) { 344 *offset = 0; 345 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 346} 347 348static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 349 SkPicture picture; 350 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 351 canvas->drawBitmap(bitmap, 0, 0); 352 SkDynamicMemoryWStream wStream; 353 picture.serialize(&wStream, &encode_bitmap_to_data); 354 return wStream.copyToData(); 355} 356 357struct ErrorContext { 358 int fErrors; 359 skiatest::Reporter* fReporter; 360}; 361 362static void assert_one_parse_error_cb(SkError error, void* context) { 363 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 364 errorContext->fErrors++; 365 // This test only expects one error, and that is a kParseError. If there are others, 366 // there is some unknown problem. 367 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 368 "This threw more errors than expected."); 369 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 370 SkGetLastErrorString()); 371} 372 373static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 374 // Create a bitmap that will be encoded. 375 SkBitmap original; 376 make_bm(&original, 100, 100, SK_ColorBLUE, true); 377 SkDynamicMemoryWStream wStream; 378 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 379 return; 380 } 381 SkAutoDataUnref data(wStream.copyToData()); 382 383 SkBitmap bm; 384 bool installSuccess = SkInstallDiscardablePixelRef( 385 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL); 386 REPORTER_ASSERT(reporter, installSuccess); 387 388 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 389 // Flattening original will follow the old path of performing an encode, while flattening bm 390 // will use the already encoded data. 391 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 392 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 393 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 394 // Now test that a parse error was generated when trying to create a new SkPicture without 395 // providing a function to decode the bitmap. 396 ErrorContext context; 397 context.fErrors = 0; 398 context.fReporter = reporter; 399 SkSetErrorCallback(assert_one_parse_error_cb, &context); 400 SkMemoryStream pictureStream(picture1); 401 SkClearLastError(); 402 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 403 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 404 SkClearLastError(); 405 SkSetErrorCallback(NULL, NULL); 406} 407 408static void test_clone_empty(skiatest::Reporter* reporter) { 409 // This is a regression test for crbug.com/172062 410 // Before the fix, we used to crash accessing a null pointer when we 411 // had a picture with no paints. This test passes by not crashing. 412 { 413 SkPicture picture; 414 picture.beginRecording(1, 1); 415 picture.endRecording(); 416 SkPicture* destPicture = picture.clone(); 417 REPORTER_ASSERT(reporter, NULL != destPicture); 418 destPicture->unref(); 419 } 420 { 421 // Test without call to endRecording 422 SkPicture picture; 423 picture.beginRecording(1, 1); 424 SkPicture* destPicture = picture.clone(); 425 REPORTER_ASSERT(reporter, NULL != destPicture); 426 destPicture->unref(); 427 } 428} 429 430static void test_clip_bound_opt(skiatest::Reporter* reporter) { 431 // Test for crbug.com/229011 432 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 433 SkIntToScalar(2), SkIntToScalar(2)); 434 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 435 SkIntToScalar(1), SkIntToScalar(1)); 436 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 437 SkIntToScalar(1), SkIntToScalar(1)); 438 439 SkPath invPath; 440 invPath.addOval(rect1); 441 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 442 SkPath path; 443 path.addOval(rect2); 444 SkPath path2; 445 path2.addOval(rect3); 446 SkIRect clipBounds; 447 // Minimalist test set for 100% code coverage of 448 // SkPictureRecord::updateClipConservativelyUsingBounds 449 { 450 SkPicture picture; 451 SkCanvas* canvas = picture.beginRecording(10, 10, 452 SkPicture::kUsePathBoundsForClip_RecordingFlag); 453 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 454 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 455 REPORTER_ASSERT(reporter, true == nonEmpty); 456 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 457 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 458 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 459 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 460 } 461 { 462 SkPicture picture; 463 SkCanvas* canvas = picture.beginRecording(10, 10, 464 SkPicture::kUsePathBoundsForClip_RecordingFlag); 465 canvas->clipPath(path, SkRegion::kIntersect_Op); 466 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 467 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 468 REPORTER_ASSERT(reporter, true == nonEmpty); 469 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 470 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 471 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 472 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 473 } 474 { 475 SkPicture picture; 476 SkCanvas* canvas = picture.beginRecording(10, 10, 477 SkPicture::kUsePathBoundsForClip_RecordingFlag); 478 canvas->clipPath(path, SkRegion::kIntersect_Op); 479 canvas->clipPath(invPath, SkRegion::kUnion_Op); 480 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 481 REPORTER_ASSERT(reporter, true == nonEmpty); 482 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 483 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 484 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 485 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 486 } 487 { 488 SkPicture picture; 489 SkCanvas* canvas = picture.beginRecording(10, 10, 490 SkPicture::kUsePathBoundsForClip_RecordingFlag); 491 canvas->clipPath(path, SkRegion::kDifference_Op); 492 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 493 REPORTER_ASSERT(reporter, true == nonEmpty); 494 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 495 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 496 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 497 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 498 } 499 { 500 SkPicture picture; 501 SkCanvas* canvas = picture.beginRecording(10, 10, 502 SkPicture::kUsePathBoundsForClip_RecordingFlag); 503 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 504 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 505 // True clip is actually empty in this case, but the best 506 // determination we can make using only bounds as input is that the 507 // clip is included in the bounds of 'path'. 508 REPORTER_ASSERT(reporter, true == nonEmpty); 509 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 510 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 511 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 512 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 513 } 514 { 515 SkPicture picture; 516 SkCanvas* canvas = picture.beginRecording(10, 10, 517 SkPicture::kUsePathBoundsForClip_RecordingFlag); 518 canvas->clipPath(path, SkRegion::kIntersect_Op); 519 canvas->clipPath(path2, SkRegion::kXOR_Op); 520 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 521 REPORTER_ASSERT(reporter, true == nonEmpty); 522 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 523 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 524 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 525 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 526 } 527} 528 529/** 530 * A canvas that records the number of clip commands. 531 */ 532class ClipCountingCanvas : public SkCanvas { 533public: 534 explicit ClipCountingCanvas(SkBaseDevice* device) 535 : SkCanvas(device) 536 , fClipCount(0){ 537 } 538 539 virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA) 540 SK_OVERRIDE { 541 fClipCount += 1; 542 return this->INHERITED::clipRect(r, op, doAA); 543 } 544 545 virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) 546 SK_OVERRIDE { 547 fClipCount += 1; 548 return this->INHERITED::clipRRect(rrect, op, doAA); 549 } 550 551 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) 552 SK_OVERRIDE { 553 fClipCount += 1; 554 return this->INHERITED::clipPath(path, op, doAA); 555 } 556 557 unsigned getClipCount() const { return fClipCount; } 558 559private: 560 unsigned fClipCount; 561 562 typedef SkCanvas INHERITED; 563}; 564 565static void test_clip_expansion(skiatest::Reporter* reporter) { 566 SkPicture picture; 567 SkCanvas* canvas = picture.beginRecording(10, 10, 0); 568 569 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 570 // The following expanding clip should not be skipped. 571 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 572 // Draw something so the optimizer doesn't just fold the world. 573 SkPaint p; 574 p.setColor(SK_ColorBLUE); 575 canvas->drawPaint(p); 576 577 SkBitmapDevice testDevice(SkBitmap::kNo_Config, 10, 10); 578 ClipCountingCanvas testCanvas(&testDevice); 579 picture.draw(&testCanvas); 580 581 // Both clips should be present on playback. 582 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 583} 584 585static void test_hierarchical(skiatest::Reporter* reporter) { 586 SkBitmap bm; 587 make_bm(&bm, 10, 10, SK_ColorRED, true); 588 589 SkCanvas* canvas; 590 591 SkPicture childPlain; 592 childPlain.beginRecording(10, 10); 593 childPlain.endRecording(); 594 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0 595 596 SkPicture childWithBitmap; 597 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 598 childWithBitmap.endRecording(); 599 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1 600 601 SkPicture parentPP; 602 canvas = parentPP.beginRecording(10, 10); 603 canvas->drawPicture(childPlain); 604 parentPP.endRecording(); 605 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0 606 607 SkPicture parentPWB; 608 canvas = parentPWB.beginRecording(10, 10); 609 canvas->drawPicture(childWithBitmap); 610 parentPWB.endRecording(); 611 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1 612 613 SkPicture parentWBP; 614 canvas = parentWBP.beginRecording(10, 10); 615 canvas->drawBitmap(bm, 0, 0); 616 canvas->drawPicture(childPlain); 617 parentWBP.endRecording(); 618 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1 619 620 SkPicture parentWBWB; 621 canvas = parentWBWB.beginRecording(10, 10); 622 canvas->drawBitmap(bm, 0, 0); 623 canvas->drawPicture(childWithBitmap); 624 parentWBWB.endRecording(); 625 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2 626} 627 628DEF_TEST(Picture, reporter) { 629#ifdef SK_DEBUG 630 test_deleting_empty_playback(); 631 test_serializing_empty_picture(); 632#else 633 test_bad_bitmap(); 634#endif 635 test_peephole(); 636 test_gatherpixelrefs(reporter); 637 test_bitmap_with_encoded_data(reporter); 638 test_clone_empty(reporter); 639 test_clip_bound_opt(reporter); 640 test_clip_expansion(reporter); 641 test_hierarchical(reporter); 642} 643