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