PictureTest.cpp revision 5bee0543f0a42aff90f725fb313b34ed702f44d8
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 "SkBitmapDevice.h" 9#include "SkCanvas.h" 10#include "SkColorPriv.h" 11#include "SkData.h" 12#include "SkDecodingImageGenerator.h" 13#include "SkError.h" 14#include "SkImageEncoder.h" 15#include "SkImageGenerator.h" 16#include "SkPaint.h" 17#include "SkPicture.h" 18#include "SkPictureUtils.h" 19#include "SkRRect.h" 20#include "SkRandom.h" 21#include "SkShader.h" 22#include "SkStream.h" 23#include "Test.h" 24 25static const int gColorScale = 30; 26static const int gColorOffset = 60; 27 28static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 29 bm->allocN32Pixels(w, h); 30 bm->eraseColor(color); 31 if (immutable) { 32 bm->setImmutable(); 33 } 34} 35 36static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) { 37 SkASSERT(w % 2 == 0); 38 SkASSERT(h % 2 == 0); 39 bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, 40 kPremul_SkAlphaType)); 41 SkAutoLockPixels lock(*bm); 42 for (int y = 0; y < h; y += 2) { 43 uint8_t* s = bm->getAddr8(0, y); 44 for (int x = 0; x < w; x += 2) { 45 *s++ = 0xFF; 46 *s++ = 0x00; 47 } 48 s = bm->getAddr8(0, y + 1); 49 for (int x = 0; x < w; x += 2) { 50 *s++ = 0x00; 51 *s++ = 0xFF; 52 } 53 } 54 if (immutable) { 55 bm->setImmutable(); 56 } 57} 58 59static void init_paint(SkPaint* paint, const SkBitmap &bm) { 60 SkShader* shader = SkShader::CreateBitmapShader(bm, 61 SkShader::kClamp_TileMode, 62 SkShader::kClamp_TileMode); 63 paint->setShader(shader)->unref(); 64} 65 66typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, 67 const SkBitmap&, const SkPoint&, 68 SkTDArray<SkPixelRef*>* usedPixRefs); 69 70static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm, 71 const SkBitmap& altBM, const SkPoint& pos, 72 SkTDArray<SkPixelRef*>* usedPixRefs) { 73 SkPaint paint; 74 init_paint(&paint, bm); 75 76 canvas->drawPaint(paint); 77 *usedPixRefs->append() = bm.pixelRef(); 78} 79 80static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm, 81 const SkBitmap& altBM, const SkPoint& pos, 82 SkTDArray<SkPixelRef*>* usedPixRefs) { 83 SkPaint paint; 84 init_paint(&paint, bm); 85 86 // draw a rect 87 SkPoint points[5] = { 88 { pos.fX, pos.fY }, 89 { pos.fX + bm.width() - 1, pos.fY }, 90 { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 }, 91 { pos.fX, pos.fY + bm.height() - 1 }, 92 { pos.fX, pos.fY }, 93 }; 94 95 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint); 96 *usedPixRefs->append() = bm.pixelRef(); 97} 98 99static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm, 100 const SkBitmap& altBM, const SkPoint& pos, 101 SkTDArray<SkPixelRef*>* usedPixRefs) { 102 SkPaint paint; 103 init_paint(&paint, bm); 104 105 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 106 r.offset(pos.fX, pos.fY); 107 108 canvas->drawRect(r, paint); 109 *usedPixRefs->append() = bm.pixelRef(); 110} 111 112static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm, 113 const SkBitmap& altBM, const SkPoint& pos, 114 SkTDArray<SkPixelRef*>* usedPixRefs) { 115 SkPaint paint; 116 init_paint(&paint, bm); 117 118 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 119 r.offset(pos.fX, pos.fY); 120 121 canvas->drawOval(r, paint); 122 *usedPixRefs->append() = bm.pixelRef(); 123} 124 125static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm, 126 const SkBitmap& altBM, const SkPoint& pos, 127 SkTDArray<SkPixelRef*>* usedPixRefs) { 128 SkPaint paint; 129 init_paint(&paint, bm); 130 131 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 132 r.offset(pos.fX, pos.fY); 133 134 SkRRect rr; 135 rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4); 136 canvas->drawRRect(rr, paint); 137 *usedPixRefs->append() = bm.pixelRef(); 138} 139 140static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm, 141 const SkBitmap& altBM, const SkPoint& pos, 142 SkTDArray<SkPixelRef*>* usedPixRefs) { 143 SkPaint paint; 144 init_paint(&paint, bm); 145 146 SkPath path; 147 path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height())); 148 path.lineTo(SkIntToScalar(bm.width()), 0); 149 path.close(); 150 path.offset(pos.fX, pos.fY); 151 152 canvas->drawPath(path, paint); 153 *usedPixRefs->append() = bm.pixelRef(); 154} 155 156static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 157 const SkBitmap& altBM, const SkPoint& pos, 158 SkTDArray<SkPixelRef*>* usedPixRefs) { 159 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 160 *usedPixRefs->append() = bm.pixelRef(); 161} 162 163static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 164 const SkBitmap& altBM, const SkPoint& pos, 165 SkTDArray<SkPixelRef*>* usedPixRefs) { 166 SkPaint paint; 167 init_paint(&paint, bm); 168 169 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 170 canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint); 171 *usedPixRefs->append() = bm.pixelRef(); 172 *usedPixRefs->append() = altBM.pixelRef(); 173} 174 175static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm, 176 const SkBitmap& altBM, const SkPoint& pos, 177 SkTDArray<SkPixelRef*>* usedPixRefs) { 178 const SkMatrix& ctm = canvas->getTotalMatrix(); 179 180 SkPoint p(pos); 181 ctm.mapPoints(&p, 1); 182 183 canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL); 184 *usedPixRefs->append() = bm.pixelRef(); 185} 186 187#if 0 188// Although specifiable, this case doesn't seem to make sense (i.e., the 189// bitmap in the shader is never used). 190static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 191 const SkBitmap& altBM, const SkPoint& pos, 192 SkTDArray<SkPixelRef*>* usedPixRefs) { 193 SkPaint paint; 194 init_paint(&paint, bm); 195 196 const SkMatrix& ctm = canvas->getTotalMatrix(); 197 198 SkPoint p(pos); 199 ctm.mapPoints(&p, 1); 200 201 canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint); 202 *usedPixRefs->append() = bm.pixelRef(); 203 *usedPixRefs->append() = altBM.pixelRef(); 204} 205#endif 206 207static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 208 const SkBitmap& altBM, const SkPoint& pos, 209 SkTDArray<SkPixelRef*>* usedPixRefs) { 210 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 211 212 r.offset(pos.fX, pos.fY); 213 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 214 *usedPixRefs->append() = bm.pixelRef(); 215} 216 217static void drawbitmaprect_withshader_proc(SkCanvas* canvas, 218 const SkBitmap& bm, 219 const SkBitmap& altBM, 220 const SkPoint& pos, 221 SkTDArray<SkPixelRef*>* usedPixRefs) { 222 SkPaint paint; 223 init_paint(&paint, bm); 224 225 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 226 r.offset(pos.fX, pos.fY); 227 228 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 229 canvas->drawBitmapRectToRect(altBM, NULL, r, &paint); 230 *usedPixRefs->append() = bm.pixelRef(); 231 *usedPixRefs->append() = altBM.pixelRef(); 232} 233 234static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm, 235 const SkBitmap& altBM, const SkPoint& pos, 236 SkTDArray<SkPixelRef*>* usedPixRefs) { 237 SkPaint paint; 238 init_paint(&paint, bm); 239 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 240 241 canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint); 242 *usedPixRefs->append() = bm.pixelRef(); 243} 244 245static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm, 246 const SkBitmap& altBM, const SkPoint& pos, 247 SkTDArray<SkPixelRef*>* usedPixRefs) { 248 SkPaint paint; 249 init_paint(&paint, bm); 250 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 251 252 SkPoint point = { pos.fX, pos.fY + bm.height() }; 253 canvas->drawPosText("O", 1, &point, paint); 254 *usedPixRefs->append() = bm.pixelRef(); 255} 256 257static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm, 258 const SkBitmap& altBM, const SkPoint& pos, 259 SkTDArray<SkPixelRef*>* usedPixRefs) { 260 SkPaint paint; 261 262 init_paint(&paint, bm); 263 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 264 265 SkPath path; 266 path.lineTo(SkIntToScalar(bm.width()), 0); 267 path.offset(pos.fX, pos.fY+bm.height()); 268 269 canvas->drawTextOnPath("O", 1, path, NULL, paint); 270 *usedPixRefs->append() = bm.pixelRef(); 271} 272 273static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm, 274 const SkBitmap& altBM, const SkPoint& pos, 275 SkTDArray<SkPixelRef*>* usedPixRefs) { 276 SkPaint paint; 277 init_paint(&paint, bm); 278 279 SkPoint verts[4] = { 280 { pos.fX, pos.fY }, 281 { pos.fX + bm.width(), pos.fY }, 282 { pos.fX + bm.width(), pos.fY + bm.height() }, 283 { pos.fX, pos.fY + bm.height() } 284 }; 285 SkPoint texs[4] = { { 0, 0 }, 286 { SkIntToScalar(bm.width()), 0 }, 287 { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }, 288 { 0, SkIntToScalar(bm.height()) } }; 289 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; 290 291 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL, 292 indices, 6, paint); 293 *usedPixRefs->append() = bm.pixelRef(); 294} 295 296// Return a picture with the bitmaps drawn at the specified positions. 297static SkPicture* record_bitmaps(const SkBitmap bm[], 298 const SkPoint pos[], 299 SkTDArray<SkPixelRef*> analytic[], 300 int count, 301 DrawBitmapProc proc) { 302 SkPicture* pic = new SkPicture; 303 SkCanvas* canvas = pic->beginRecording(1000, 1000); 304 for (int i = 0; i < count; ++i) { 305 analytic[i].rewind(); 306 canvas->save(); 307 SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, 308 SkIntToScalar(bm[i].width()), 309 SkIntToScalar(bm[i].height())); 310 canvas->clipRect(clipRect, SkRegion::kIntersect_Op); 311 proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]); 312 canvas->restore(); 313 } 314 pic->endRecording(); 315 return pic; 316} 317 318static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { 319 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 320 rect->fTop = rand.nextRangeScalar(-H, 2*H); 321 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 322 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 323 324 // we integralize rect to make our tests more predictable, since Gather is 325 // a little sloppy. 326 SkIRect ir; 327 rect->round(&ir); 328 rect->set(ir); 329} 330 331static void draw(SkPicture* pic, int width, int height, SkBitmap* result) { 332 make_bm(result, width, height, SK_ColorBLACK, false); 333 334 SkCanvas canvas(*result); 335 canvas.drawPicture(*pic); 336} 337 338template <typename T> int find_index(const T* array, T elem, int count) { 339 for (int i = 0; i < count; ++i) { 340 if (array[i] == elem) { 341 return i; 342 } 343 } 344 return -1; 345} 346 347// Return true if 'ref' is found in array[] 348static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 349 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 350} 351 352// Look at each pixel that is inside 'subset', and if its color appears in 353// colors[], find the corresponding value in refs[] and append that ref into 354// array, skipping duplicates of the same value. 355// Note that gathering pixelRefs from rendered colors suffers from the problem 356// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color) 357// isn't easy to reconstruct. 358static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[], 359 int count, SkTDArray<SkPixelRef*>* array, 360 const SkRect& subset) { 361 SkIRect ir; 362 subset.roundOut(&ir); 363 364 if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) { 365 return; 366 } 367 368 // Since we only want to return unique values in array, when we scan we just 369 // set a bit for each index'd color found. In practice we only have a few 370 // distinct colors, so we just use an int's bits as our array. Hence the 371 // assert that count <= number-of-bits-in-our-int. 372 SkASSERT((unsigned)count <= 32); 373 uint32_t bitarray = 0; 374 375 SkAutoLockPixels alp(bm); 376 377 for (int y = ir.fTop; y < ir.fBottom; ++y) { 378 for (int x = ir.fLeft; x < ir.fRight; ++x) { 379 SkPMColor pmc = *bm.getAddr32(x, y); 380 // the only good case where the color is not found would be if 381 // the color is transparent, meaning no bitmap was drawn in that 382 // pixel. 383 if (pmc) { 384 uint32_t index = SkGetPackedR32(pmc); 385 SkASSERT(SkGetPackedG32(pmc) == index); 386 SkASSERT(SkGetPackedB32(pmc) == index); 387 if (0 == index) { 388 continue; // background color 389 } 390 SkASSERT(0 == (index - gColorOffset) % gColorScale); 391 index = (index - gColorOffset) / gColorScale; 392 SkASSERT(static_cast<int>(index) < count); 393 bitarray |= 1 << index; 394 } 395 } 396 } 397 398 for (int i = 0; i < count; ++i) { 399 if (bitarray & (1 << i)) { 400 *array->append() = refs[i]; 401 } 402 } 403} 404 405static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h, 406 const SkTDArray<SkPixelRef*> analytic[], 407 int count, 408 SkTDArray<SkPixelRef*>* result, 409 const SkRect& subset) { 410 for (int i = 0; i < count; ++i) { 411 SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h); 412 413 if (SkRect::Intersects(subset, rect)) { 414 result->append(analytic[i].count(), analytic[i].begin()); 415 } 416 } 417} 418 419 420static const struct { 421 const DrawBitmapProc proc; 422 const char* const desc; 423} gProcs[] = { 424 {drawpaint_proc, "drawpaint"}, 425 {drawpoints_proc, "drawpoints"}, 426 {drawrect_proc, "drawrect"}, 427 {drawoval_proc, "drawoval"}, 428 {drawrrect_proc, "drawrrect"}, 429 {drawpath_proc, "drawpath"}, 430 {drawbitmap_proc, "drawbitmap"}, 431 {drawbitmap_withshader_proc, "drawbitmap_withshader"}, 432 {drawsprite_proc, "drawsprite"}, 433#if 0 434 {drawsprite_withshader_proc, "drawsprite_withshader"}, 435#endif 436 {drawbitmaprect_proc, "drawbitmaprect"}, 437 {drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"}, 438 {drawtext_proc, "drawtext"}, 439 {drawpostext_proc, "drawpostext"}, 440 {drawtextonpath_proc, "drawtextonpath"}, 441 {drawverts_proc, "drawverts"}, 442}; 443 444static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) { 445 // Our convention is that the color components contain an encoding of 446 // the index of their corresponding bitmap/pixelref. (0,0,0,0) is 447 // reserved for the background 448 for (int i = 0; i < num; ++i) { 449 make_bm(&bm[i], w, h, 450 SkColorSetARGB(0xFF, 451 gColorScale*i+gColorOffset, 452 gColorScale*i+gColorOffset, 453 gColorScale*i+gColorOffset), 454 true); 455 refs[i] = bm[i].pixelRef(); 456 } 457 458 // The A8 alternate bitmaps are all BW checkerboards 459 for (int i = 0; i < num; ++i) { 460 make_checkerboard(&bm[num+i], w, h, true); 461 refs[num+i] = bm[num+i].pixelRef(); 462 } 463} 464 465static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 466 const int IW = 32; 467 const int IH = IW; 468 const SkScalar W = SkIntToScalar(IW); 469 const SkScalar H = W; 470 471 static const int N = 4; 472 SkBitmap bm[2*N]; 473 SkPixelRef* refs[2*N]; 474 SkTDArray<SkPixelRef*> analytic[N]; 475 476 const SkPoint pos[N] = { 477 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 478 }; 479 480 create_textures(bm, refs, N, IW, IH); 481 482 SkRandom rand; 483 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 484 SkAutoTUnref<SkPicture> pic( 485 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 486 487 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 488 // quick check for a small piece of each quadrant, which should just 489 // contain 1 or 2 bitmaps. 490 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 491 SkRect r; 492 r.set(2, 2, W - 2, H - 2); 493 r.offset(pos[i].fX, pos[i].fY); 494 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 495 if (!data) { 496 ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned " 497 "NULL for %s.", gProcs[k].desc); 498 continue; 499 } 500 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data(); 501 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); 502 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 503 if (1 == count) { 504 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 505 } else if (2 == count) { 506 REPORTER_ASSERT(reporter, 507 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 508 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 509 } 510 } 511 512 SkBitmap image; 513 draw(pic, 2*IW, 2*IH, &image); 514 515 // Test a bunch of random (mostly) rects, and compare the gather results 516 // with a deduced list of refs by looking at the colors drawn. 517 for (int j = 0; j < 100; ++j) { 518 SkRect r; 519 rand_rect(&r, rand, 2*W, 2*H); 520 521 SkTDArray<SkPixelRef*> fromImage; 522 gather_from_image(image, refs, N, &fromImage, r); 523 524 SkTDArray<SkPixelRef*> fromAnalytic; 525 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 526 527 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 528 size_t dataSize = data ? data->size() : 0; 529 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); 530 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 531 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 532 SkAutoDataUnref adu(data); 533 534 // Everything that we saw drawn should appear in the analytic list 535 // but the analytic list may contain some pixelRefs that were not 536 // seen in the image (e.g., A8 textures used as masks) 537 for (int i = 0; i < fromImage.count(); ++i) { 538 if (-1 == fromAnalytic.find(fromImage[i])) { 539 ERRORF(reporter, "PixelRef missing %d %s", 540 i, gProcs[k].desc); 541 } 542 } 543 544 /* 545 * GatherPixelRefs is conservative, so it can return more bitmaps 546 * than are strictly required. Thus our check here is only that 547 * Gather didn't miss any that we actually needed. Even that isn't 548 * a strict requirement on Gather, which is meant to be quick and 549 * only mostly-correct, but at the moment this test should work. 550 */ 551 for (int i = 0; i < fromAnalytic.count(); ++i) { 552 bool found = find(gatherRefs, fromAnalytic[i], gatherCount); 553 if (!found) { 554 ERRORF(reporter, "PixelRef missing %d %s", 555 i, gProcs[k].desc); 556 } 557#if 0 558 // enable this block of code to debug failures, as it will rerun 559 // the case that failed. 560 if (!found) { 561 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 562 size_t dataSize = data ? data->size() : 0; 563 } 564#endif 565 } 566 } 567 } 568} 569 570static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) { 571 const int IW = 32; 572 const int IH = IW; 573 const SkScalar W = SkIntToScalar(IW); 574 const SkScalar H = W; 575 576 static const int N = 4; 577 SkBitmap bm[2*N]; 578 SkPixelRef* refs[2*N]; 579 SkTDArray<SkPixelRef*> analytic[N]; 580 581 const SkPoint pos[N] = { 582 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 583 }; 584 585 create_textures(bm, refs, N, IW, IH); 586 587 SkRandom rand; 588 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 589 SkAutoTUnref<SkPicture> pic( 590 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 591 592 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 593 594 SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont( 595 new SkPictureUtils::SkPixelRefsAndRectsList); 596 597 SkPictureUtils::GatherPixelRefsAndRects(pic, prCont); 598 599 // quick check for a small piece of each quadrant, which should just 600 // contain 1 or 2 bitmaps. 601 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 602 SkRect r; 603 r.set(2, 2, W - 2, H - 2); 604 r.offset(pos[i].fX, pos[i].fY); 605 606 SkTDArray<SkPixelRef*> gatheredRefs; 607 prCont->query(r, &gatheredRefs); 608 609 int count = gatheredRefs.count(); 610 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 611 if (1 == count) { 612 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 613 } else if (2 == count) { 614 REPORTER_ASSERT(reporter, 615 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 616 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 617 } 618 } 619 620 SkBitmap image; 621 draw(pic, 2*IW, 2*IH, &image); 622 623 // Test a bunch of random (mostly) rects, and compare the gather results 624 // with the analytic results and the pixel refs seen in a rendering. 625 for (int j = 0; j < 100; ++j) { 626 SkRect r; 627 rand_rect(&r, rand, 2*W, 2*H); 628 629 SkTDArray<SkPixelRef*> fromImage; 630 gather_from_image(image, refs, N, &fromImage, r); 631 632 SkTDArray<SkPixelRef*> fromAnalytic; 633 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 634 635 SkTDArray<SkPixelRef*> gatheredRefs; 636 prCont->query(r, &gatheredRefs); 637 638 // Everything that we saw drawn should appear in the analytic list 639 // but the analytic list may contain some pixelRefs that were not 640 // seen in the image (e.g., A8 textures used as masks) 641 for (int i = 0; i < fromImage.count(); ++i) { 642 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); 643 } 644 645 // Everything in the analytic list should appear in the gathered 646 // list. 647 for (int i = 0; i < fromAnalytic.count(); ++i) { 648 REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i])); 649 } 650 } 651 } 652} 653 654#ifdef SK_DEBUG 655// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 656// run in debug mode. 657static void test_deleting_empty_playback() { 658 SkPicture picture; 659 // Creates an SkPictureRecord 660 picture.beginRecording(0, 0); 661 // Turns that into an SkPicturePlayback 662 picture.endRecording(); 663 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 664 picture.beginRecording(0, 0); 665} 666 667// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 668static void test_serializing_empty_picture() { 669 SkPicture picture; 670 picture.beginRecording(0, 0); 671 picture.endRecording(); 672 SkDynamicMemoryWStream stream; 673 picture.serialize(&stream); 674} 675#endif 676 677static void rand_op(SkCanvas* canvas, SkRandom& rand) { 678 SkPaint paint; 679 SkRect rect = SkRect::MakeWH(50, 50); 680 681 SkScalar unit = rand.nextUScalar1(); 682 if (unit <= 0.3) { 683// SkDebugf("save\n"); 684 canvas->save(); 685 } else if (unit <= 0.6) { 686// SkDebugf("restore\n"); 687 canvas->restore(); 688 } else if (unit <= 0.9) { 689// SkDebugf("clip\n"); 690 canvas->clipRect(rect); 691 } else { 692// SkDebugf("draw\n"); 693 canvas->drawPaint(paint); 694 } 695} 696 697static void set_canvas_to_save_count_4(SkCanvas* canvas) { 698 canvas->restoreToCount(1); 699 canvas->save(); 700 canvas->save(); 701 canvas->save(); 702} 703 704static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 705 SkCanvas testCanvas(100, 100); 706 set_canvas_to_save_count_4(&testCanvas); 707 708 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 709 710 SkPaint paint; 711 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 712 713 SkPicture extra_save_picture; 714 extra_save_picture.beginRecording(100, 100); 715 extra_save_picture.getRecordingCanvas()->save(); 716 extra_save_picture.getRecordingCanvas()->translate(10, 10); 717 extra_save_picture.getRecordingCanvas()->drawRect(rect, paint); 718 extra_save_picture.getRecordingCanvas()->save(); 719 extra_save_picture.getRecordingCanvas()->translate(10, 10); 720 extra_save_picture.getRecordingCanvas()->drawRect(rect, paint); 721 722 testCanvas.drawPicture(extra_save_picture); 723 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 724 725 set_canvas_to_save_count_4(&testCanvas); 726 727 SkPicture extra_restore_picture; 728 extra_restore_picture.beginRecording(100, 100); 729 extra_restore_picture.getRecordingCanvas()->save(); 730 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 731 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 732 extra_restore_picture.getRecordingCanvas()->save(); 733 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 734 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 735 extra_restore_picture.getRecordingCanvas()->restore(); 736 extra_restore_picture.getRecordingCanvas()->restore(); 737 extra_restore_picture.getRecordingCanvas()->restore(); 738 extra_restore_picture.getRecordingCanvas()->restore(); 739 740 testCanvas.drawPicture(extra_save_picture); 741 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 742 743 SkPicture no_save_picture; 744 extra_restore_picture.beginRecording(100, 100); 745 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 746 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 747 748 testCanvas.drawPicture(extra_save_picture); 749 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 750 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 751} 752 753static void test_peephole() { 754 SkRandom rand; 755 756 for (int j = 0; j < 100; j++) { 757 SkRandom rand2(rand); // remember the seed 758 759 SkPicture picture; 760 SkCanvas* canvas = picture.beginRecording(100, 100); 761 762 for (int i = 0; i < 1000; ++i) { 763 rand_op(canvas, rand); 764 } 765 picture.endRecording(); 766 767 rand = rand2; 768 } 769 770 { 771 SkPicture picture; 772 SkCanvas* canvas = picture.beginRecording(100, 100); 773 SkRect rect = SkRect::MakeWH(50, 50); 774 775 for (int i = 0; i < 100; ++i) { 776 canvas->save(); 777 } 778 while (canvas->getSaveCount() > 1) { 779 canvas->clipRect(rect); 780 canvas->restore(); 781 } 782 picture.endRecording(); 783 } 784} 785 786#ifndef SK_DEBUG 787// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 788// should never do this. 789static void test_bad_bitmap() { 790 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 791 // fail. 792 SkBitmap bm; 793 bm.setConfig(SkImageInfo::MakeN32Premul(100, 100)); 794 SkPicture picture; 795 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 796 recordingCanvas->drawBitmap(bm, 0, 0); 797 picture.endRecording(); 798 799 SkCanvas canvas; 800 canvas.drawPicture(picture); 801} 802#endif 803 804static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 805 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 806} 807 808static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 809 SkPicture picture; 810 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 811 canvas->drawBitmap(bitmap, 0, 0); 812 SkDynamicMemoryWStream wStream; 813 picture.serialize(&wStream, &encode_bitmap_to_data); 814 return wStream.copyToData(); 815} 816 817struct ErrorContext { 818 int fErrors; 819 skiatest::Reporter* fReporter; 820}; 821 822static void assert_one_parse_error_cb(SkError error, void* context) { 823 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 824 errorContext->fErrors++; 825 // This test only expects one error, and that is a kParseError. If there are others, 826 // there is some unknown problem. 827 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 828 "This threw more errors than expected."); 829 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 830 SkGetLastErrorString()); 831} 832 833static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 834 // Create a bitmap that will be encoded. 835 SkBitmap original; 836 make_bm(&original, 100, 100, SK_ColorBLUE, true); 837 SkDynamicMemoryWStream wStream; 838 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 839 return; 840 } 841 SkAutoDataUnref data(wStream.copyToData()); 842 843 SkBitmap bm; 844 bool installSuccess = SkInstallDiscardablePixelRef( 845 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL); 846 REPORTER_ASSERT(reporter, installSuccess); 847 848 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 849 // Flattening original will follow the old path of performing an encode, while flattening bm 850 // will use the already encoded data. 851 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 852 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 853 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 854 // Now test that a parse error was generated when trying to create a new SkPicture without 855 // providing a function to decode the bitmap. 856 ErrorContext context; 857 context.fErrors = 0; 858 context.fReporter = reporter; 859 SkSetErrorCallback(assert_one_parse_error_cb, &context); 860 SkMemoryStream pictureStream(picture1); 861 SkClearLastError(); 862 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 863 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 864 SkClearLastError(); 865 SkSetErrorCallback(NULL, NULL); 866} 867 868static void test_clone_empty(skiatest::Reporter* reporter) { 869 // This is a regression test for crbug.com/172062 870 // Before the fix, we used to crash accessing a null pointer when we 871 // had a picture with no paints. This test passes by not crashing. 872 { 873 SkPicture picture; 874 picture.beginRecording(1, 1); 875 picture.endRecording(); 876 SkPicture* destPicture = picture.clone(); 877 REPORTER_ASSERT(reporter, NULL != destPicture); 878 destPicture->unref(); 879 } 880 { 881 // Test without call to endRecording 882 SkPicture picture; 883 picture.beginRecording(1, 1); 884 SkPicture* destPicture = picture.clone(); 885 REPORTER_ASSERT(reporter, NULL != destPicture); 886 destPicture->unref(); 887 } 888} 889 890static void test_clip_bound_opt(skiatest::Reporter* reporter) { 891 // Test for crbug.com/229011 892 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 893 SkIntToScalar(2), SkIntToScalar(2)); 894 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 895 SkIntToScalar(1), SkIntToScalar(1)); 896 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 897 SkIntToScalar(1), SkIntToScalar(1)); 898 899 SkPath invPath; 900 invPath.addOval(rect1); 901 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 902 SkPath path; 903 path.addOval(rect2); 904 SkPath path2; 905 path2.addOval(rect3); 906 SkIRect clipBounds; 907 // Minimalist test set for 100% code coverage of 908 // SkPictureRecord::updateClipConservativelyUsingBounds 909 { 910 SkPicture picture; 911 SkCanvas* canvas = picture.beginRecording(10, 10, 912 SkPicture::kUsePathBoundsForClip_RecordingFlag); 913 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 914 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 915 REPORTER_ASSERT(reporter, true == nonEmpty); 916 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 917 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 918 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 919 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 920 } 921 { 922 SkPicture picture; 923 SkCanvas* canvas = picture.beginRecording(10, 10, 924 SkPicture::kUsePathBoundsForClip_RecordingFlag); 925 canvas->clipPath(path, SkRegion::kIntersect_Op); 926 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 927 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 928 REPORTER_ASSERT(reporter, true == nonEmpty); 929 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 930 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 931 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 932 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 933 } 934 { 935 SkPicture picture; 936 SkCanvas* canvas = picture.beginRecording(10, 10, 937 SkPicture::kUsePathBoundsForClip_RecordingFlag); 938 canvas->clipPath(path, SkRegion::kIntersect_Op); 939 canvas->clipPath(invPath, SkRegion::kUnion_Op); 940 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 941 REPORTER_ASSERT(reporter, true == nonEmpty); 942 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 943 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 944 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 945 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 946 } 947 { 948 SkPicture picture; 949 SkCanvas* canvas = picture.beginRecording(10, 10, 950 SkPicture::kUsePathBoundsForClip_RecordingFlag); 951 canvas->clipPath(path, SkRegion::kDifference_Op); 952 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 953 REPORTER_ASSERT(reporter, true == nonEmpty); 954 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 955 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 956 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 957 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 958 } 959 { 960 SkPicture picture; 961 SkCanvas* canvas = picture.beginRecording(10, 10, 962 SkPicture::kUsePathBoundsForClip_RecordingFlag); 963 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 964 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 965 // True clip is actually empty in this case, but the best 966 // determination we can make using only bounds as input is that the 967 // clip is included in the bounds of 'path'. 968 REPORTER_ASSERT(reporter, true == nonEmpty); 969 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 970 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 971 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 972 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 973 } 974 { 975 SkPicture picture; 976 SkCanvas* canvas = picture.beginRecording(10, 10, 977 SkPicture::kUsePathBoundsForClip_RecordingFlag); 978 canvas->clipPath(path, SkRegion::kIntersect_Op); 979 canvas->clipPath(path2, SkRegion::kXOR_Op); 980 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 981 REPORTER_ASSERT(reporter, true == nonEmpty); 982 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 983 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 984 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 985 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 986 } 987} 988 989/** 990 * A canvas that records the number of clip commands. 991 */ 992class ClipCountingCanvas : public SkCanvas { 993public: 994 explicit ClipCountingCanvas(int width, int height) 995 : INHERITED(width, height) 996 , fClipCount(0){ 997 } 998 999 virtual void onClipRect(const SkRect& r, 1000 SkRegion::Op op, 1001 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1002 fClipCount += 1; 1003 this->INHERITED::onClipRect(r, op, edgeStyle); 1004 } 1005 1006 virtual void onClipRRect(const SkRRect& rrect, 1007 SkRegion::Op op, 1008 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 1009 fClipCount += 1; 1010 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1011 } 1012 1013 virtual void onClipPath(const SkPath& path, 1014 SkRegion::Op op, 1015 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1016 fClipCount += 1; 1017 this->INHERITED::onClipPath(path, op, edgeStyle); 1018 } 1019 1020 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1021 fClipCount += 1; 1022 this->INHERITED::onClipRegion(deviceRgn, op); 1023 } 1024 1025 unsigned getClipCount() const { return fClipCount; } 1026 1027private: 1028 unsigned fClipCount; 1029 1030 typedef SkCanvas INHERITED; 1031}; 1032 1033static void test_clip_expansion(skiatest::Reporter* reporter) { 1034 SkPicture picture; 1035 SkCanvas* canvas = picture.beginRecording(10, 10, 0); 1036 1037 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1038 // The following expanding clip should not be skipped. 1039 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1040 // Draw something so the optimizer doesn't just fold the world. 1041 SkPaint p; 1042 p.setColor(SK_ColorBLUE); 1043 canvas->drawPaint(p); 1044 1045 ClipCountingCanvas testCanvas(10, 10); 1046 picture.draw(&testCanvas); 1047 1048 // Both clips should be present on playback. 1049 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1050} 1051 1052static void test_hierarchical(skiatest::Reporter* reporter) { 1053 SkBitmap bm; 1054 make_bm(&bm, 10, 10, SK_ColorRED, true); 1055 1056 SkCanvas* canvas; 1057 1058 SkPicture childPlain; 1059 childPlain.beginRecording(10, 10); 1060 childPlain.endRecording(); 1061 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0 1062 1063 SkPicture childWithBitmap; 1064 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1065 childWithBitmap.endRecording(); 1066 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1 1067 1068 SkPicture parentPP; 1069 canvas = parentPP.beginRecording(10, 10); 1070 canvas->drawPicture(childPlain); 1071 parentPP.endRecording(); 1072 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0 1073 1074 SkPicture parentPWB; 1075 canvas = parentPWB.beginRecording(10, 10); 1076 canvas->drawPicture(childWithBitmap); 1077 parentPWB.endRecording(); 1078 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1 1079 1080 SkPicture parentWBP; 1081 canvas = parentWBP.beginRecording(10, 10); 1082 canvas->drawBitmap(bm, 0, 0); 1083 canvas->drawPicture(childPlain); 1084 parentWBP.endRecording(); 1085 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1 1086 1087 SkPicture parentWBWB; 1088 canvas = parentWBWB.beginRecording(10, 10); 1089 canvas->drawBitmap(bm, 0, 0); 1090 canvas->drawPicture(childWithBitmap); 1091 parentWBWB.endRecording(); 1092 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2 1093} 1094 1095DEF_TEST(Picture, reporter) { 1096#ifdef SK_DEBUG 1097 test_deleting_empty_playback(); 1098 test_serializing_empty_picture(); 1099#else 1100 test_bad_bitmap(); 1101#endif 1102 test_unbalanced_save_restores(reporter); 1103 test_peephole(); 1104 test_gatherpixelrefs(reporter); 1105 test_gatherpixelrefsandrects(reporter); 1106 test_bitmap_with_encoded_data(reporter); 1107 test_clone_empty(reporter); 1108 test_clip_bound_opt(reporter); 1109 test_clip_expansion(reporter); 1110 test_hierarchical(reporter); 1111} 1112 1113static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1114 const SkPaint paint; 1115 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1116 const SkIRect irect = { 2, 2, 3, 3 }; 1117 1118 // Don't care what these record, as long as they're legal. 1119 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1120 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1121 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1122 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1123 canvas->drawSprite(bitmap, 1, 1); 1124} 1125 1126static void test_draw_bitmaps(SkCanvas* canvas) { 1127 SkBitmap empty; 1128 draw_bitmaps(empty, canvas); 1129 empty.setConfig(SkImageInfo::MakeN32Premul(10, 10)); 1130 draw_bitmaps(empty, canvas); 1131} 1132 1133DEF_TEST(Picture_EmptyBitmap, r) { 1134 SkPicture picture; 1135 test_draw_bitmaps(picture.beginRecording(10, 10)); 1136 picture.endRecording(); 1137} 1138 1139DEF_TEST(Canvas_EmptyBitmap, r) { 1140 SkBitmap dst; 1141 dst.allocN32Pixels(10, 10); 1142 SkCanvas canvas(dst); 1143 1144 test_draw_bitmaps(&canvas); 1145} 1146