PictureTest.cpp revision 8f90a892c5130d4d26b5588e1ff151d01a40688a
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 set_canvas_to_save_count_4(SkCanvas* canvas) { 684 canvas->restoreToCount(1); 685 canvas->save(); 686 canvas->save(); 687 canvas->save(); 688} 689 690static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 691 SkCanvas testCanvas(100, 100); 692 set_canvas_to_save_count_4(&testCanvas); 693 694 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 695 696 SkPaint paint; 697 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 698 699 SkPicture extra_save_picture; 700 extra_save_picture.beginRecording(100, 100); 701 extra_save_picture.getRecordingCanvas()->save(); 702 extra_save_picture.getRecordingCanvas()->translate(10, 10); 703 extra_save_picture.getRecordingCanvas()->drawRect(rect, paint); 704 extra_save_picture.getRecordingCanvas()->save(); 705 extra_save_picture.getRecordingCanvas()->translate(10, 10); 706 extra_save_picture.getRecordingCanvas()->drawRect(rect, paint); 707 708 testCanvas.drawPicture(extra_save_picture); 709 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 710 711 set_canvas_to_save_count_4(&testCanvas); 712 713 SkPicture extra_restore_picture; 714 extra_restore_picture.beginRecording(100, 100); 715 extra_restore_picture.getRecordingCanvas()->save(); 716 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 717 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 718 extra_restore_picture.getRecordingCanvas()->save(); 719 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 720 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 721 extra_restore_picture.getRecordingCanvas()->restore(); 722 extra_restore_picture.getRecordingCanvas()->restore(); 723 extra_restore_picture.getRecordingCanvas()->restore(); 724 extra_restore_picture.getRecordingCanvas()->restore(); 725 726 testCanvas.drawPicture(extra_save_picture); 727 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 728 729 SkPicture no_save_picture; 730 extra_restore_picture.beginRecording(100, 100); 731 extra_restore_picture.getRecordingCanvas()->translate(10, 10); 732 extra_restore_picture.getRecordingCanvas()->drawRect(rect, paint); 733 734 testCanvas.drawPicture(extra_save_picture); 735 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 736 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 737} 738 739static void test_peephole() { 740 SkRandom rand; 741 742 for (int j = 0; j < 100; j++) { 743 SkRandom rand2(rand); // remember the seed 744 745 SkPicture picture; 746 SkCanvas* canvas = picture.beginRecording(100, 100); 747 748 for (int i = 0; i < 1000; ++i) { 749 rand_op(canvas, rand); 750 } 751 picture.endRecording(); 752 753 rand = rand2; 754 } 755 756 { 757 SkPicture picture; 758 SkCanvas* canvas = picture.beginRecording(100, 100); 759 SkRect rect = SkRect::MakeWH(50, 50); 760 761 for (int i = 0; i < 100; ++i) { 762 canvas->save(); 763 } 764 while (canvas->getSaveCount() > 1) { 765 canvas->clipRect(rect); 766 canvas->restore(); 767 } 768 picture.endRecording(); 769 } 770} 771 772#ifndef SK_DEBUG 773// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 774// should never do this. 775static void test_bad_bitmap() { 776 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 777 // fail. 778 SkBitmap bm; 779 bm.setConfig(SkImageInfo::MakeN32Premul(100, 100)); 780 SkPicture picture; 781 SkCanvas* recordingCanvas = picture.beginRecording(100, 100); 782 recordingCanvas->drawBitmap(bm, 0, 0); 783 picture.endRecording(); 784 785 SkCanvas canvas; 786 canvas.drawPicture(picture); 787} 788#endif 789 790static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 791 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 792} 793 794static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 795 SkPicture picture; 796 SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height()); 797 canvas->drawBitmap(bitmap, 0, 0); 798 SkDynamicMemoryWStream wStream; 799 picture.serialize(&wStream, &encode_bitmap_to_data); 800 return wStream.copyToData(); 801} 802 803struct ErrorContext { 804 int fErrors; 805 skiatest::Reporter* fReporter; 806}; 807 808static void assert_one_parse_error_cb(SkError error, void* context) { 809 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 810 errorContext->fErrors++; 811 // This test only expects one error, and that is a kParseError. If there are others, 812 // there is some unknown problem. 813 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 814 "This threw more errors than expected."); 815 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 816 SkGetLastErrorString()); 817} 818 819static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 820 // Create a bitmap that will be encoded. 821 SkBitmap original; 822 make_bm(&original, 100, 100, SK_ColorBLUE, true); 823 SkDynamicMemoryWStream wStream; 824 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 825 return; 826 } 827 SkAutoDataUnref data(wStream.copyToData()); 828 829 SkBitmap bm; 830 bool installSuccess = SkInstallDiscardablePixelRef( 831 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL); 832 REPORTER_ASSERT(reporter, installSuccess); 833 834 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 835 // Flattening original will follow the old path of performing an encode, while flattening bm 836 // will use the already encoded data. 837 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 838 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 839 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 840 // Now test that a parse error was generated when trying to create a new SkPicture without 841 // providing a function to decode the bitmap. 842 ErrorContext context; 843 context.fErrors = 0; 844 context.fReporter = reporter; 845 SkSetErrorCallback(assert_one_parse_error_cb, &context); 846 SkMemoryStream pictureStream(picture1); 847 SkClearLastError(); 848 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 849 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 850 SkClearLastError(); 851 SkSetErrorCallback(NULL, NULL); 852} 853 854static void test_clone_empty(skiatest::Reporter* reporter) { 855 // This is a regression test for crbug.com/172062 856 // Before the fix, we used to crash accessing a null pointer when we 857 // had a picture with no paints. This test passes by not crashing. 858 { 859 SkPicture picture; 860 picture.beginRecording(1, 1); 861 picture.endRecording(); 862 SkPicture* destPicture = picture.clone(); 863 REPORTER_ASSERT(reporter, NULL != destPicture); 864 destPicture->unref(); 865 } 866 { 867 // Test without call to endRecording 868 SkPicture picture; 869 picture.beginRecording(1, 1); 870 SkPicture* destPicture = picture.clone(); 871 REPORTER_ASSERT(reporter, NULL != destPicture); 872 destPicture->unref(); 873 } 874} 875 876static void test_clip_bound_opt(skiatest::Reporter* reporter) { 877 // Test for crbug.com/229011 878 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 879 SkIntToScalar(2), SkIntToScalar(2)); 880 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 881 SkIntToScalar(1), SkIntToScalar(1)); 882 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 883 SkIntToScalar(1), SkIntToScalar(1)); 884 885 SkPath invPath; 886 invPath.addOval(rect1); 887 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 888 SkPath path; 889 path.addOval(rect2); 890 SkPath path2; 891 path2.addOval(rect3); 892 SkIRect clipBounds; 893 // Minimalist test set for 100% code coverage of 894 // SkPictureRecord::updateClipConservativelyUsingBounds 895 { 896 SkPicture picture; 897 SkCanvas* canvas = picture.beginRecording(10, 10, 898 SkPicture::kUsePathBoundsForClip_RecordingFlag); 899 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 900 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 901 REPORTER_ASSERT(reporter, true == nonEmpty); 902 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 903 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 904 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 905 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 906 } 907 { 908 SkPicture picture; 909 SkCanvas* canvas = picture.beginRecording(10, 10, 910 SkPicture::kUsePathBoundsForClip_RecordingFlag); 911 canvas->clipPath(path, SkRegion::kIntersect_Op); 912 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 913 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 914 REPORTER_ASSERT(reporter, true == nonEmpty); 915 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 916 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 917 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 918 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 919 } 920 { 921 SkPicture picture; 922 SkCanvas* canvas = picture.beginRecording(10, 10, 923 SkPicture::kUsePathBoundsForClip_RecordingFlag); 924 canvas->clipPath(path, SkRegion::kIntersect_Op); 925 canvas->clipPath(invPath, SkRegion::kUnion_Op); 926 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 927 REPORTER_ASSERT(reporter, true == nonEmpty); 928 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 929 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 930 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 931 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 932 } 933 { 934 SkPicture picture; 935 SkCanvas* canvas = picture.beginRecording(10, 10, 936 SkPicture::kUsePathBoundsForClip_RecordingFlag); 937 canvas->clipPath(path, SkRegion::kDifference_Op); 938 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 939 REPORTER_ASSERT(reporter, true == nonEmpty); 940 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 941 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 942 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 943 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 944 } 945 { 946 SkPicture picture; 947 SkCanvas* canvas = picture.beginRecording(10, 10, 948 SkPicture::kUsePathBoundsForClip_RecordingFlag); 949 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 950 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 951 // True clip is actually empty in this case, but the best 952 // determination we can make using only bounds as input is that the 953 // clip is included in the bounds of 'path'. 954 REPORTER_ASSERT(reporter, true == nonEmpty); 955 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 956 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 957 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 958 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 959 } 960 { 961 SkPicture picture; 962 SkCanvas* canvas = picture.beginRecording(10, 10, 963 SkPicture::kUsePathBoundsForClip_RecordingFlag); 964 canvas->clipPath(path, SkRegion::kIntersect_Op); 965 canvas->clipPath(path2, SkRegion::kXOR_Op); 966 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 967 REPORTER_ASSERT(reporter, true == nonEmpty); 968 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 969 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 970 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 971 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 972 } 973} 974 975/** 976 * A canvas that records the number of clip commands. 977 */ 978class ClipCountingCanvas : public SkCanvas { 979public: 980 explicit ClipCountingCanvas(int width, int height) 981 : INHERITED(width, height) 982 , fClipCount(0){ 983 } 984 985 virtual void onClipRect(const SkRect& r, 986 SkRegion::Op op, 987 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 988 fClipCount += 1; 989 this->INHERITED::onClipRect(r, op, edgeStyle); 990 } 991 992 virtual void onClipRRect(const SkRRect& rrect, 993 SkRegion::Op op, 994 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 995 fClipCount += 1; 996 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 997 } 998 999 virtual void onClipPath(const SkPath& path, 1000 SkRegion::Op op, 1001 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1002 fClipCount += 1; 1003 this->INHERITED::onClipPath(path, op, edgeStyle); 1004 } 1005 1006 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1007 fClipCount += 1; 1008 this->INHERITED::onClipRegion(deviceRgn, op); 1009 } 1010 1011 unsigned getClipCount() const { return fClipCount; } 1012 1013private: 1014 unsigned fClipCount; 1015 1016 typedef SkCanvas INHERITED; 1017}; 1018 1019static void test_clip_expansion(skiatest::Reporter* reporter) { 1020 SkPicture picture; 1021 SkCanvas* canvas = picture.beginRecording(10, 10, 0); 1022 1023 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1024 // The following expanding clip should not be skipped. 1025 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1026 // Draw something so the optimizer doesn't just fold the world. 1027 SkPaint p; 1028 p.setColor(SK_ColorBLUE); 1029 canvas->drawPaint(p); 1030 1031 ClipCountingCanvas testCanvas(10, 10); 1032 picture.draw(&testCanvas); 1033 1034 // Both clips should be present on playback. 1035 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1036} 1037 1038static void test_hierarchical(skiatest::Reporter* reporter) { 1039 SkBitmap bm; 1040 make_bm(&bm, 10, 10, SK_ColorRED, true); 1041 1042 SkCanvas* canvas; 1043 1044 SkPicture childPlain; 1045 childPlain.beginRecording(10, 10); 1046 childPlain.endRecording(); 1047 REPORTER_ASSERT(reporter, !childPlain.willPlayBackBitmaps()); // 0 1048 1049 SkPicture childWithBitmap; 1050 childWithBitmap.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1051 childWithBitmap.endRecording(); 1052 REPORTER_ASSERT(reporter, childWithBitmap.willPlayBackBitmaps()); // 1 1053 1054 SkPicture parentPP; 1055 canvas = parentPP.beginRecording(10, 10); 1056 canvas->drawPicture(childPlain); 1057 parentPP.endRecording(); 1058 REPORTER_ASSERT(reporter, !parentPP.willPlayBackBitmaps()); // 0 1059 1060 SkPicture parentPWB; 1061 canvas = parentPWB.beginRecording(10, 10); 1062 canvas->drawPicture(childWithBitmap); 1063 parentPWB.endRecording(); 1064 REPORTER_ASSERT(reporter, parentPWB.willPlayBackBitmaps()); // 1 1065 1066 SkPicture parentWBP; 1067 canvas = parentWBP.beginRecording(10, 10); 1068 canvas->drawBitmap(bm, 0, 0); 1069 canvas->drawPicture(childPlain); 1070 parentWBP.endRecording(); 1071 REPORTER_ASSERT(reporter, parentWBP.willPlayBackBitmaps()); // 1 1072 1073 SkPicture parentWBWB; 1074 canvas = parentWBWB.beginRecording(10, 10); 1075 canvas->drawBitmap(bm, 0, 0); 1076 canvas->drawPicture(childWithBitmap); 1077 parentWBWB.endRecording(); 1078 REPORTER_ASSERT(reporter, parentWBWB.willPlayBackBitmaps()); // 2 1079} 1080 1081DEF_TEST(Picture, reporter) { 1082#ifdef SK_DEBUG 1083 test_deleting_empty_playback(); 1084 test_serializing_empty_picture(); 1085#else 1086 test_bad_bitmap(); 1087#endif 1088 test_unbalanced_save_restores(reporter); 1089 test_peephole(); 1090 test_gatherpixelrefs(reporter); 1091 test_gatherpixelrefsandrects(reporter); 1092 test_bitmap_with_encoded_data(reporter); 1093 test_clone_empty(reporter); 1094 test_clip_bound_opt(reporter); 1095 test_clip_expansion(reporter); 1096 test_hierarchical(reporter); 1097} 1098 1099static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1100 const SkPaint paint; 1101 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1102 const SkIRect irect = { 2, 2, 3, 3 }; 1103 1104 // Don't care what these record, as long as they're legal. 1105 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1106 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1107 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1108 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1109 canvas->drawSprite(bitmap, 1, 1); 1110} 1111 1112static void test_draw_bitmaps(SkCanvas* canvas) { 1113 SkBitmap empty; 1114 draw_bitmaps(empty, canvas); 1115 empty.setConfig(SkImageInfo::MakeN32Premul(10, 10)); 1116 draw_bitmaps(empty, canvas); 1117} 1118 1119DEF_TEST(Picture_EmptyBitmap, r) { 1120 SkPicture picture; 1121 test_draw_bitmaps(picture.beginRecording(10, 10)); 1122 picture.endRecording(); 1123} 1124 1125DEF_TEST(Canvas_EmptyBitmap, r) { 1126 SkBitmap dst; 1127 dst.allocN32Pixels(10, 10); 1128 SkCanvas canvas(dst); 1129 1130 test_draw_bitmaps(&canvas); 1131} 1132