PictureTest.cpp revision deee496cd30070e52556dcb538c2e5eb39b66b81
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 419static const DrawBitmapProc gProcs[] = { 420 drawpaint_proc, 421 drawpoints_proc, 422 drawrect_proc, 423 drawoval_proc, 424 drawrrect_proc, 425 drawpath_proc, 426 drawbitmap_proc, 427 drawbitmap_withshader_proc, 428 drawsprite_proc, 429#if 0 430 drawsprite_withshader_proc, 431#endif 432 drawbitmaprect_proc, 433 drawbitmaprect_withshader_proc, 434 drawtext_proc, 435 drawpostext_proc, 436 drawtextonpath_proc, 437 drawverts_proc, 438}; 439 440static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) { 441 // Our convention is that the color components contain an encoding of 442 // the index of their corresponding bitmap/pixelref. (0,0,0,0) is 443 // reserved for the background 444 for (int i = 0; i < num; ++i) { 445 make_bm(&bm[i], w, h, 446 SkColorSetARGB(0xFF, 447 gColorScale*i+gColorOffset, 448 gColorScale*i+gColorOffset, 449 gColorScale*i+gColorOffset), 450 true); 451 refs[i] = bm[i].pixelRef(); 452 } 453 454 // The A8 alternate bitmaps are all BW checkerboards 455 for (int i = 0; i < num; ++i) { 456 make_checkerboard(&bm[num+i], w, h, true); 457 refs[num+i] = bm[num+i].pixelRef(); 458 } 459} 460 461static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 462 const int IW = 32; 463 const int IH = IW; 464 const SkScalar W = SkIntToScalar(IW); 465 const SkScalar H = W; 466 467 static const int N = 4; 468 SkBitmap bm[2*N]; 469 SkPixelRef* refs[2*N]; 470 SkTDArray<SkPixelRef*> analytic[N]; 471 472 const SkPoint pos[N] = { 473 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 474 }; 475 476 create_textures(bm, refs, N, IW, IH); 477 478 SkRandom rand; 479 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 480 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k])); 481 482 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 483 // quick check for a small piece of each quadrant, which should just 484 // contain 1 or 2 bitmaps. 485 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 486 SkRect r; 487 r.set(2, 2, W - 2, H - 2); 488 r.offset(pos[i].fX, pos[i].fY); 489 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 490 REPORTER_ASSERT(reporter, data); 491 if (data) { 492 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data(); 493 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); 494 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 495 if (1 == count) { 496 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 497 } else if (2 == count) { 498 REPORTER_ASSERT(reporter, 499 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 500 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 501 } 502 } 503 } 504 505 SkBitmap image; 506 draw(pic, 2*IW, 2*IH, &image); 507 508 // Test a bunch of random (mostly) rects, and compare the gather results 509 // with a deduced list of refs by looking at the colors drawn. 510 for (int j = 0; j < 100; ++j) { 511 SkRect r; 512 rand_rect(&r, rand, 2*W, 2*H); 513 514 SkTDArray<SkPixelRef*> fromImage; 515 gather_from_image(image, refs, N, &fromImage, r); 516 517 SkTDArray<SkPixelRef*> fromAnalytic; 518 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 519 520 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 521 size_t dataSize = data ? data->size() : 0; 522 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); 523 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 524 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 525 SkAutoDataUnref adu(data); 526 527 // Everything that we saw drawn should appear in the analytic list 528 // but the analytic list may contain some pixelRefs that were not 529 // seen in the image (e.g., A8 textures used as masks) 530 for (int i = 0; i < fromImage.count(); ++i) { 531 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); 532 } 533 534 /* 535 * GatherPixelRefs is conservative, so it can return more bitmaps 536 * than are strictly required. Thus our check here is only that 537 * Gather didn't miss any that we actually needed. Even that isn't 538 * a strict requirement on Gather, which is meant to be quick and 539 * only mostly-correct, but at the moment this test should work. 540 */ 541 for (int i = 0; i < fromAnalytic.count(); ++i) { 542 bool found = find(gatherRefs, fromAnalytic[i], gatherCount); 543 REPORTER_ASSERT(reporter, found); 544#if 0 545 // enable this block of code to debug failures, as it will rerun 546 // the case that failed. 547 if (!found) { 548 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 549 size_t dataSize = data ? data->size() : 0; 550 } 551#endif 552 } 553 } 554 } 555} 556 557static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) { 558 const int IW = 32; 559 const int IH = IW; 560 const SkScalar W = SkIntToScalar(IW); 561 const SkScalar H = W; 562 563 static const int N = 4; 564 SkBitmap bm[2*N]; 565 SkPixelRef* refs[2*N]; 566 SkTDArray<SkPixelRef*> analytic[N]; 567 568 const SkPoint pos[N] = { 569 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 570 }; 571 572 create_textures(bm, refs, N, IW, IH); 573 574 SkRandom rand; 575 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 576 SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k])); 577 578 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 579 580 SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont( 581 new SkPictureUtils::SkPixelRefsAndRectsList); 582 583 SkPictureUtils::GatherPixelRefsAndRects(pic, prCont); 584 585 // quick check for a small piece of each quadrant, which should just 586 // contain 1 or 2 bitmaps. 587 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 588 SkRect r; 589 r.set(2, 2, W - 2, H - 2); 590 r.offset(pos[i].fX, pos[i].fY); 591 592 SkTDArray<SkPixelRef*> gatheredRefs; 593 prCont->query(r, &gatheredRefs); 594 595 int count = gatheredRefs.count(); 596 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 597 if (1 == count) { 598 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 599 } else if (2 == count) { 600 REPORTER_ASSERT(reporter, 601 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 602 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 603 } 604 } 605 606 SkBitmap image; 607 draw(pic, 2*IW, 2*IH, &image); 608 609 // Test a bunch of random (mostly) rects, and compare the gather results 610 // with the analytic results and the pixel refs seen in a rendering. 611 for (int j = 0; j < 100; ++j) { 612 SkRect r; 613 rand_rect(&r, rand, 2*W, 2*H); 614 615 SkTDArray<SkPixelRef*> fromImage; 616 gather_from_image(image, refs, N, &fromImage, r); 617 618 SkTDArray<SkPixelRef*> fromAnalytic; 619 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 620 621 SkTDArray<SkPixelRef*> gatheredRefs; 622 prCont->query(r, &gatheredRefs); 623 624 // Everything that we saw drawn should appear in the analytic list 625 // but the analytic list may contain some pixelRefs that were not 626 // seen in the image (e.g., A8 textures used as masks) 627 for (int i = 0; i < fromImage.count(); ++i) { 628 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); 629 } 630 631 // Everything in the analytic list should appear in the gathered 632 // list. 633 for (int i = 0; i < fromAnalytic.count(); ++i) { 634 REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i])); 635 } 636 } 637 } 638} 639 640#ifdef SK_DEBUG 641// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 642// run in debug mode. 643static void test_deleting_empty_playback() { 644 SkPicture picture; 645 // Creates an SkPictureRecord 646 picture.beginRecording(0, 0); 647 // Turns that into an SkPicturePlayback 648 picture.endRecording(); 649 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 650 picture.beginRecording(0, 0); 651} 652 653// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 654static void test_serializing_empty_picture() { 655 SkPicture picture; 656 picture.beginRecording(0, 0); 657 picture.endRecording(); 658 SkDynamicMemoryWStream stream; 659 picture.serialize(&stream); 660} 661#endif 662 663static void rand_op(SkCanvas* canvas, SkRandom& rand) { 664 SkPaint paint; 665 SkRect rect = SkRect::MakeWH(50, 50); 666 667 SkScalar unit = rand.nextUScalar1(); 668 if (unit <= 0.3) { 669// SkDebugf("save\n"); 670 canvas->save(); 671 } else if (unit <= 0.6) { 672// SkDebugf("restore\n"); 673 canvas->restore(); 674 } else if (unit <= 0.9) { 675// SkDebugf("clip\n"); 676 canvas->clipRect(rect); 677 } else { 678// SkDebugf("draw\n"); 679 canvas->drawPaint(paint); 680 } 681} 682 683static void test_peephole() { 684 SkRandom rand; 685 686 for (int j = 0; j < 100; j++) { 687 SkRandom rand2(rand); // remember the seed 688 689 SkPicture picture; 690 SkCanvas* canvas = picture.beginRecording(100, 100); 691 692 for (int i = 0; i < 1000; ++i) { 693 rand_op(canvas, rand); 694 } 695 picture.endRecording(); 696 697 rand = rand2; 698 } 699 700 { 701 SkPicture picture; 702 SkCanvas* canvas = picture.beginRecording(100, 100); 703 SkRect rect = SkRect::MakeWH(50, 50); 704 705 for (int i = 0; i < 100; ++i) { 706 canvas->save(); 707 } 708 while (canvas->getSaveCount() > 1) { 709 canvas->clipRect(rect); 710 canvas->restore(); 711 } 712 picture.endRecording(); 713 } 714} 715 716#ifndef SK_DEBUG 717// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 718// should never do this. 719static void test_bad_bitmap() { 720 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 721 // fail. 722 SkBitmap bm; 723 bm.setConfig(SkImageInfo::MakeN32Premul(100, 100)); 724 SkPicture picture; 725 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 726 recordingCanvas->drawBitmap(bm, 0, 0); 727 picture.endRecording(); 728 729 SkCanvas canvas; 730 canvas.drawPicture(picture); 731} 732#endif 733 734static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 735 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 736} 737 738static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 739 SkPicture picture; 740 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 741 canvas->drawBitmap(bitmap, 0, 0); 742 SkDynamicMemoryWStream wStream; 743 picture.serialize(&wStream, &encode_bitmap_to_data); 744 return wStream.copyToData(); 745} 746 747struct ErrorContext { 748 int fErrors; 749 skiatest::Reporter* fReporter; 750}; 751 752static void assert_one_parse_error_cb(SkError error, void* context) { 753 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 754 errorContext->fErrors++; 755 // This test only expects one error, and that is a kParseError. If there are others, 756 // there is some unknown problem. 757 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 758 "This threw more errors than expected."); 759 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 760 SkGetLastErrorString()); 761} 762 763static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 764 // Create a bitmap that will be encoded. 765 SkBitmap original; 766 make_bm(&original, 100, 100, SK_ColorBLUE, true); 767 SkDynamicMemoryWStream wStream; 768 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 769 return; 770 } 771 SkAutoDataUnref data(wStream.copyToData()); 772 773 SkBitmap bm; 774 bool installSuccess = SkInstallDiscardablePixelRef( 775 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL); 776 REPORTER_ASSERT(reporter, installSuccess); 777 778 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 779 // Flattening original will follow the old path of performing an encode, while flattening bm 780 // will use the already encoded data. 781 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 782 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 783 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 784 // Now test that a parse error was generated when trying to create a new SkPicture without 785 // providing a function to decode the bitmap. 786 ErrorContext context; 787 context.fErrors = 0; 788 context.fReporter = reporter; 789 SkSetErrorCallback(assert_one_parse_error_cb, &context); 790 SkMemoryStream pictureStream(picture1); 791 SkClearLastError(); 792 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 793 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 794 SkClearLastError(); 795 SkSetErrorCallback(NULL, NULL); 796} 797 798static void test_clone_empty(skiatest::Reporter* reporter) { 799 // This is a regression test for crbug.com/172062 800 // Before the fix, we used to crash accessing a null pointer when we 801 // had a picture with no paints. This test passes by not crashing. 802 { 803 SkPicture picture; 804 picture.beginRecording(1, 1); 805 picture.endRecording(); 806 SkPicture* destPicture = picture.clone(); 807 REPORTER_ASSERT(reporter, NULL != destPicture); 808 destPicture->unref(); 809 } 810 { 811 // Test without call to endRecording 812 SkPicture picture; 813 picture.beginRecording(1, 1); 814 SkPicture* destPicture = picture.clone(); 815 REPORTER_ASSERT(reporter, NULL != destPicture); 816 destPicture->unref(); 817 } 818} 819 820static void test_clip_bound_opt(skiatest::Reporter* reporter) { 821 // Test for crbug.com/229011 822 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 823 SkIntToScalar(2), SkIntToScalar(2)); 824 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 825 SkIntToScalar(1), SkIntToScalar(1)); 826 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 827 SkIntToScalar(1), SkIntToScalar(1)); 828 829 SkPath invPath; 830 invPath.addOval(rect1); 831 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 832 SkPath path; 833 path.addOval(rect2); 834 SkPath path2; 835 path2.addOval(rect3); 836 SkIRect clipBounds; 837 // Minimalist test set for 100% code coverage of 838 // SkPictureRecord::updateClipConservativelyUsingBounds 839 { 840 SkPicture picture; 841 SkCanvas* canvas = picture.beginRecording(10, 10, 842 SkPicture::kUsePathBoundsForClip_RecordingFlag); 843 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 844 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 845 REPORTER_ASSERT(reporter, true == nonEmpty); 846 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 847 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 848 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 849 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 850 } 851 { 852 SkPicture picture; 853 SkCanvas* canvas = picture.beginRecording(10, 10, 854 SkPicture::kUsePathBoundsForClip_RecordingFlag); 855 canvas->clipPath(path, SkRegion::kIntersect_Op); 856 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 857 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 858 REPORTER_ASSERT(reporter, true == nonEmpty); 859 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 860 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 861 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 862 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 863 } 864 { 865 SkPicture picture; 866 SkCanvas* canvas = picture.beginRecording(10, 10, 867 SkPicture::kUsePathBoundsForClip_RecordingFlag); 868 canvas->clipPath(path, SkRegion::kIntersect_Op); 869 canvas->clipPath(invPath, SkRegion::kUnion_Op); 870 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 871 REPORTER_ASSERT(reporter, true == nonEmpty); 872 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 873 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 874 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 875 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 876 } 877 { 878 SkPicture picture; 879 SkCanvas* canvas = picture.beginRecording(10, 10, 880 SkPicture::kUsePathBoundsForClip_RecordingFlag); 881 canvas->clipPath(path, SkRegion::kDifference_Op); 882 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 883 REPORTER_ASSERT(reporter, true == nonEmpty); 884 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 885 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 886 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 887 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 888 } 889 { 890 SkPicture picture; 891 SkCanvas* canvas = picture.beginRecording(10, 10, 892 SkPicture::kUsePathBoundsForClip_RecordingFlag); 893 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 894 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 895 // True clip is actually empty in this case, but the best 896 // determination we can make using only bounds as input is that the 897 // clip is included in the bounds of 'path'. 898 REPORTER_ASSERT(reporter, true == nonEmpty); 899 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 900 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 901 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 902 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 903 } 904 { 905 SkPicture picture; 906 SkCanvas* canvas = picture.beginRecording(10, 10, 907 SkPicture::kUsePathBoundsForClip_RecordingFlag); 908 canvas->clipPath(path, SkRegion::kIntersect_Op); 909 canvas->clipPath(path2, SkRegion::kXOR_Op); 910 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 911 REPORTER_ASSERT(reporter, true == nonEmpty); 912 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 913 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 914 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 915 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 916 } 917} 918 919/** 920 * A canvas that records the number of clip commands. 921 */ 922class ClipCountingCanvas : public SkCanvas { 923public: 924 explicit ClipCountingCanvas(int width, int height) 925 : INHERITED(width, height) 926 , fClipCount(0){ 927 } 928 929 virtual bool clipRect(const SkRect& r, SkRegion::Op op, bool doAA) 930 SK_OVERRIDE { 931 fClipCount += 1; 932 return this->INHERITED::clipRect(r, op, doAA); 933 } 934 935 virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) 936 SK_OVERRIDE { 937 fClipCount += 1; 938 return this->INHERITED::clipRRect(rrect, op, doAA); 939 } 940 941 virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) 942 SK_OVERRIDE { 943 fClipCount += 1; 944 return this->INHERITED::clipPath(path, op, doAA); 945 } 946 947 unsigned getClipCount() const { return fClipCount; } 948 949private: 950 unsigned fClipCount; 951 952 typedef SkCanvas INHERITED; 953}; 954 955static void test_clip_expansion(skiatest::Reporter* reporter) { 956 SkPicture picture; 957 SkCanvas* canvas = picture.beginRecording(10, 10, 0); 958 959 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 960 // The following expanding clip should not be skipped. 961 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 962 // Draw something so the optimizer doesn't just fold the world. 963 SkPaint p; 964 p.setColor(SK_ColorBLUE); 965 canvas->drawPaint(p); 966 967 ClipCountingCanvas testCanvas(10, 10); 968 picture.draw(&testCanvas); 969 970 // Both clips should be present on playback. 971 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 972} 973 974static void test_hierarchical(skiatest::Reporter* reporter) { 975 SkBitmap bm; 976 make_bm(&bm, 10, 10, SK_ColorRED, true); 977 978 SkCanvas* canvas; 979 980 SkPicture childPlain; 981 childPlain.beginRecording(10, 10); 982 childPlain.endRecording(); 983 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0 984 985 SkPicture childWithBitmap; 986 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 987 childWithBitmap.endRecording(); 988 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1 989 990 SkPicture parentPP; 991 canvas = parentPP.beginRecording(10, 10); 992 canvas->drawPicture(childPlain); 993 parentPP.endRecording(); 994 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0 995 996 SkPicture parentPWB; 997 canvas = parentPWB.beginRecording(10, 10); 998 canvas->drawPicture(childWithBitmap); 999 parentPWB.endRecording(); 1000 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1 1001 1002 SkPicture parentWBP; 1003 canvas = parentWBP.beginRecording(10, 10); 1004 canvas->drawBitmap(bm, 0, 0); 1005 canvas->drawPicture(childPlain); 1006 parentWBP.endRecording(); 1007 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1 1008 1009 SkPicture parentWBWB; 1010 canvas = parentWBWB.beginRecording(10, 10); 1011 canvas->drawBitmap(bm, 0, 0); 1012 canvas->drawPicture(childWithBitmap); 1013 parentWBWB.endRecording(); 1014 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2 1015} 1016 1017DEF_TEST(Picture, reporter) { 1018#ifdef SK_DEBUG 1019 test_deleting_empty_playback(); 1020 test_serializing_empty_picture(); 1021#else 1022 test_bad_bitmap(); 1023#endif 1024 test_peephole(); 1025 test_gatherpixelrefs(reporter); 1026 test_gatherpixelrefsandrects(reporter); 1027 test_bitmap_with_encoded_data(reporter); 1028 test_clone_empty(reporter); 1029 test_clip_bound_opt(reporter); 1030 test_clip_expansion(reporter); 1031 test_hierarchical(reporter); 1032} 1033 1034static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1035 const SkPaint paint; 1036 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1037 const SkIRect irect = { 2, 2, 3, 3 }; 1038 1039 // Don't care what these record, as long as they're legal. 1040 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1041 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1042 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1043 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1044 canvas->drawSprite(bitmap, 1, 1); 1045} 1046 1047static void test_draw_bitmaps(SkCanvas* canvas) { 1048 SkBitmap empty; 1049 draw_bitmaps(empty, canvas); 1050 empty.setConfig(SkImageInfo::MakeN32Premul(10, 10)); 1051 draw_bitmaps(empty, canvas); 1052} 1053 1054DEF_TEST(Picture_EmptyBitmap, r) { 1055 SkPicture picture; 1056 test_draw_bitmaps(picture.beginRecording(10, 10)); 1057 picture.endRecording(); 1058} 1059 1060DEF_TEST(Canvas_EmptyBitmap, r) { 1061 SkBitmap dst; 1062 dst.allocN32Pixels(10, 10); 1063 SkCanvas canvas(dst); 1064 1065 test_draw_bitmaps(&canvas); 1066} 1067